Browse Source

update readme.md

xubo 1 year ago
parent
commit
bcc113d001
100 changed files with 20436 additions and 22 deletions
  1. 3 0
      .gitignore
  2. 28 0
      Dockerfile
  3. 16 0
      README.en.md
  4. 0 22
      README.md
  5. BIN
      __pycache__/app.cpython-310.pyc
  6. 988 0
      app.py
  7. BIN
      box_advise.png
  8. BIN
      boxes.png
  9. 21 0
      config.py
  10. 36 0
      config.yaml
  11. 694 0
      cubic/cubic_simple_action.py
  12. 6 0
      db.toml
  13. BIN
      dongchedi/__pycache__/dongchedi_sign.cpython-311.pyc
  14. BIN
      dongchedi/__pycache__/random_browse.cpython-311.pyc
  15. 243 0
      dongchedi/atom-task/confirmed_browse.py
  16. 176 0
      dongchedi/atom-task/platform_sign.py
  17. 243 0
      dongchedi/atom-task/random_browse.py
  18. 401 0
      dongchedi/atom-task/search_article_comment.py
  19. 416 0
      dongchedi/atom-task/search_article_like_comment.py
  20. 367 0
      dongchedi/complete_browse.py
  21. 258 0
      dongchedi/dongchedi_interaction.py
  22. 177 0
      dongchedi/dongchedi_sign.py
  23. BIN
      dongchedi/pic/dongchedi_sign.png
  24. 420 0
      dongchedi/random_browse.py
  25. 478 0
      dongchedi/simple_comment.py
  26. BIN
      douyin/__pycache__/douyin_boxes_advise.cpython-311.pyc
  27. BIN
      douyin/__pycache__/douyin_random_search.cpython-311.pyc
  28. BIN
      douyin/__pycache__/douyin_sign.cpython-311.pyc
  29. BIN
      douyin/__pycache__/douyin_train_home_comment.cpython-311.pyc
  30. BIN
      douyin/__pycache__/douyin_train_home_page.cpython-311.pyc
  31. BIN
      douyin/__pycache__/douyin_user_operate.cpython-311.pyc
  32. BIN
      douyin/__pycache__/douyin_watch_advise.cpython-311.pyc
  33. BIN
      douyin/__pycache__/simple_browser_video.cpython-311.pyc
  34. 488 0
      douyin/atom-task/search_atricle_comment.py
  35. 752 0
      douyin/atom-task/search_atricle_like_collection.py
  36. 8 0
      douyin/atom-task/test_poc.py
  37. 335 0
      douyin/douyin_boxes_advise.py
  38. 653 0
      douyin/douyin_comments_operate.py
  39. 660 0
      douyin/douyin_designate_user_comments_operate.py
  40. 444 0
      douyin/douyin_interaction_search.py
  41. 489 0
      douyin/douyin_interaction_urgent.py
  42. 675 0
      douyin/douyin_live_comment.py
  43. 759 0
      douyin/douyin_officail_account_interact.py
  44. 279 0
      douyin/douyin_random_search.py
  45. 661 0
      douyin/douyin_random_watch_video.py
  46. 266 0
      douyin/douyin_sign.py
  47. 497 0
      douyin/douyin_train_home_comment.py
  48. 348 0
      douyin/douyin_train_home_page.py
  49. 548 0
      douyin/douyin_user_operate.py
  50. 297 0
      douyin/douyin_watch_advise.py
  51. 407 0
      douyin/douyin_watch_noval.py
  52. 661 0
      douyin/fast_random_watch_video.py
  53. 163 0
      douyin/simple_browser_video.py
  54. BIN
      douyin_search.png
  55. 0 0
      func/__init__.py
  56. BIN
      func/__pycache__/__init__.cpython-311.pyc
  57. BIN
      func/__pycache__/action_func.cpython-311.pyc
  58. BIN
      func/__pycache__/action_tool.cpython-311.pyc
  59. 0 0
      func/action_enum.py
  60. 14 0
      func/action_func.py
  61. 785 0
      func/action_tool.py
  62. 48 0
      func/check_app.py
  63. 267 0
      func/check_device.py
  64. 20 0
      gunicorn.conf
  65. 296 0
      kuaishou/gifmaker_random_watch_video.py
  66. BIN
      kuaishou/screen/no_collected.png
  67. BIN
      noval.png
  68. BIN
      ready_advise.png
  69. 53 0
      requirements.txt
  70. 1 0
      run.sh
  71. 0 0
      scene/__init__.py
  72. BIN
      scene/__pycache__/__init__.cpython-311.pyc
  73. BIN
      scene/dongchedi/__pycache__/account_culture_job.cpython-311.pyc
  74. BIN
      scene/dongchedi/__pycache__/account_interaction_job.cpython-311.pyc
  75. BIN
      scene/dongchedi/__pycache__/account_interaction_user_job.cpython-311.pyc
  76. BIN
      scene/dongchedi/__pycache__/cheyouquan_job.cpython-311.pyc
  77. BIN
      scene/dongchedi/__pycache__/dongchedi_toutiao_response.cpython-311.pyc
  78. BIN
      scene/dongchedi/__pycache__/interaction_poc.cpython-311.pyc
  79. BIN
      scene/dongchedi/__pycache__/user_comment_job.cpython-311.pyc
  80. 311 0
      scene/dongchedi/account_circle_poc.py
  81. 367 0
      scene/dongchedi/account_culture_poc.py
  82. 115 0
      scene/dongchedi/interaction_poc.py
  83. 572 0
      scene/dongchedi/user_like_poc.py
  84. 578 0
      scene/dongchedi/user_reply_poc.py
  85. 482 0
      scene/dongchedi/user_sign_poc.py
  86. BIN
      scene/mock/__pycache__/poc_data.cpython-311.pyc
  87. BIN
      scene/oprator/__pycache__/atom_data.cpython-311.pyc
  88. 623 0
      scene/oprator/atom_data.py
  89. 657 0
      scene/wework/script_wework.py
  90. BIN
      sign.png
  91. BIN
      sign_ready.png
  92. 8 0
      sources.list
  93. 0 0
      strategy/__init__.py
  94. BIN
      strategy/__pycache__/__init__.cpython-311.pyc
  95. BIN
      strategy/__pycache__/rpa_enum.cpython-311.pyc
  96. BIN
      strategy/__pycache__/rpa_handler.cpython-311.pyc
  97. 333 0
      strategy/rpa_atom_handler.py
  98. 230 0
      strategy/rpa_enum.py
  99. 315 0
      strategy/rpa_handler.py
  100. 0 0
      task/__init__.py

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+logs
+.idea
+.pyc

+ 28 - 0
Dockerfile

@@ -0,0 +1,28 @@
+# 使用Python作为基础镜像
+FROM python:3.11
+# 设置工作目录
+WORKDIR /app
+# 复制应用代码到容器中
+COPY . /app
+# 替换源
+# RUN sed -i s/deb.debian.org/mirrors.aliyun.com/g /etc/apt/sources.list
+#ADD sources.list /etc/apt/
+# 更新
+# RUN apt-get clean
+# RUN apt-get update
+# 安装ADB
+# RUN apt-get install -y android-tools-adb telnet net-tools
+# 调整时区
+ENV TZ=Asia/Shanghai
+# 安装依赖项
+RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
+# RUN pip install --no-cache-dir -r requirements.txt -i https://repo.risingauto.com/repository/py-group/simple
+# RUN pip install -i https://pypi.douban.com/simple gunicorn mysql-connector mysql
+RUN mkdir logs
+# 暴露应用端口
+EXPOSE 80 5000 5037
+# 设置启动命令
+#CMD ["gunicorn", "app:app", "-c", "gunicorn.conf.py"]
+#CMD ["python", "app.py"]
+RUN chmod +x run.sh
+CMD ["/bin/bash", "run.sh"]

+ 16 - 0
README.en.md

@@ -0,0 +1,16 @@
+# auto-control-services
+
+#### Description
+{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
+
+#### Software Architecture
+Software architecture description
+
+
+#### Contribution
+
+1.  Fork the repository
+2.  Create Feat_xxx branch
+3.  Commit your code
+4.  Create Pull Request
+

+ 0 - 22
README.md

@@ -8,19 +8,6 @@ Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN
 #### 软件架构
 软件架构说明
 
-
-#### 安装教程
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### 使用说明
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
 #### 参与贡献
 
 1.  Fork 本仓库
@@ -28,12 +15,3 @@ Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN
 3.  提交代码
 4.  新建 Pull Request
 
-
-#### 特技
-
-1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

BIN
__pycache__/app.cpython-310.pyc


+ 988 - 0
app.py

@@ -0,0 +1,988 @@
+import json
+import threading
+import time
+
+from flask import Flask, request, make_response
+from task.task_dict import task_dict
+from strategy.rpa_handler import rpa_handler
+from strategy.rpa_atom_handler import rpa_handler as rpa_atom_handler
+
+from flask_restful import Api
+from simplejson import JSONEncoder
+
+from scene.dongchedi.interaction_poc import dong_action, dongcd_stop
+from scene.oprator.atom_data import single_click_by_control, send_text_by_control, single_click_by_text, \
+    get_content_by_control, start_app, stop_app
+from apscheduler.schedulers.background import BackgroundScheduler
+
+from task.task_job import auto_pull_task
+from tools import common_util, loggerKit, redis_client
+from tools.api_route import route
+
+app = Flask(__name__)
+
+# 实例化对象
+api = Api(app)
+
+
+# 404处理
+@app.errorhandler(404)
+def not_found(e):
+    return make_response({'code': 404, 'msg': 'not found'}, 404)
+
+
+datas = [{'id': 1, 'name': 'xag', 'age': 18}, {'id': 2, 'name': 'xingag', 'age': 19}]
+
+
+@route(app, '/', methods=['POST', 'GET'])
+def hello_world():
+    return {'lists': [0, 1, 2, 3, 4, 5]}
+
+
+@route(app, '/index', methods=['GET'])
+def index():
+    return json.dumps(datas)
+
+
+# 异常处理
+@route(app, '/error', methods=['POST', 'GET'])
+def error():
+    a = 99
+    b = 0
+    return a / b  # 除0引发异常
+
+
+@route(app, '/start_douyin', methods=['POST'])
+def start_douyin():
+    data = request.json
+    if data is not None \
+            and data.get('keyword') is not None \
+            and data.get('content') is not None \
+            and data.get('device_serials') is not None:
+        keyword = data.get('keyword')
+        content = data.get('content')
+        device_serials = data.get('device_serials')
+        # devices = device_serials.split(',')
+        # 解析 mobile、device
+        strs = device_serials.split(',')
+
+        threads = []
+        i = 0
+        while i < len(strs):
+            devices_mobiles = strs[i]
+            device_mobile = devices_mobiles.split('+')
+            # threads.append(threading.Thread(target=dou_action, args=(device_mobile[1], device_mobile[0], keyword,
+            # content,))) operator_action(devices[i], keyword, content)
+            i += 1
+
+        for t in threads:
+            print(t)
+            t.start()
+
+    else:
+        return 400
+
+    return 200
+
+
+@route(app, '/start_dong', methods=['POST'])
+def start_dong():
+    data = request.json
+    if data is not None \
+            and data.get('keyword') is not None \
+            and data.get('content') is not None \
+            and data.get('device_serials') is not None:
+        keyword = data.get('keyword')
+        content = data.get('content')
+        device_serials = data.get('device_serials')
+        # devices = device_serials.split(',')
+        # 解析 mobile、device
+        strs = device_serials.split(',')
+
+        threads = []
+        i = 0
+        while i < len(strs):
+            devices_mobiles = strs[i]
+            device_mobile = devices_mobiles.split('+')
+            threads.append(
+                threading.Thread(target=dong_action, args=(device_mobile[1], device_mobile[0], keyword, content,)))
+            i += 1
+
+        for t in threads:
+            print(t)
+            t.start()
+
+    else:
+        return 400
+
+    return 200
+
+
+@route(app, '/stop_douyin', methods=['POST'])
+def dou_stop():
+    data = request.json
+    print(data)
+    if data is not None \
+            and data.get('device_serials') is not None:
+        device_serials = data.get('device_serials')
+        if ',' in device_serials:
+            devices = device_serials.split(',')
+            threads = []
+            i = 0
+            while i < len(devices):
+                # threads.append(threading.Thread(target=douyin_stop, args=(devices[i],)))
+                i += 1
+
+            for t in threads:
+                print(t)
+                t.start()
+        else:
+            # douyin_stop(device_serials)
+            pass
+
+    else:
+        return 400
+
+    return 200
+
+
+@route(app, '/stop_dong', methods=['POST'])
+def dong_stop():
+    data = request.json
+    if data is not None \
+            and data.get('device_serials') is not None:
+        device_serials = data.get('device_serials')
+        if ',' in device_serials:
+            devices = device_serials.split(',')
+            threads = []
+            i = 0
+            while i < len(devices):
+                threads.append(threading.Thread(target=dongcd_stop, args=(devices[i],)))
+                i += 1
+
+            for t in threads:
+                print(t)
+                t.start()
+        else:
+            dongcd_stop(device_serials)
+
+    else:
+        return 400
+
+    return 200
+
+
+@route(app, '/get_ip_info', methods=['GET'])
+def get_ip_info():
+    common_util.get_ip_info('58.216.107.91')
+
+
+@route(app, '/get_local_ip', methods=['GET'])
+def query_local_ip():
+    print(common_util.get_ip_info('8.8.8.8'))
+
+
+# 领取任务接口v2
+@route(app, '/api/action_v1', methods=['POST'])
+def do_action_v1():
+    """
+    :param deviceID [必填]设备的ID
+    :param performActionId [非必填]前一条指令的ID
+    :param performActionResult success or failure
+    :param recognizedResult 你好,我是你根据控件Id查到的text内容
+    领取任务接口
+    :return: 指令json串
+    """
+
+    data = request.json
+    loggerKit.info('请求信息:{0}', data)
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if device_id is None:
+        loggerKit.info('请求信息:{0}', data)
+
+        return_dict = {
+            "data": "",
+            "code": -1,
+            "message": "fail, request device_id is null"
+        }
+
+        return return_dict
+
+    # action0_id = int(round(time.time() * 1000))
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            return return_dict
+
+        action0_id_mem = redis_client.get(device_id)
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and action0_id_mem is not None and int(perform_action_id) == int(
+                action0_id_mem) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            懂车帝APP首页点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_version="7.9.0",
+                                                   control_id="com.ss.android.auto:id/c9c")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_{action1_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        action1_id_mem = redis_client.get(device_id)
+        step1 = redis_client.get(f"{device_id}_{perform_action_id}_step1")
+        if step1 is not None and int(step1) == 1 and action1_id_mem is not None and int(perform_action_id) == int(
+                action1_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(action1_id_mem))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在搜索框输入关键词
+            """
+            action2_dict = send_text_by_control(action2_id, control_id="com.ss.android.auto:id/h5q", content="飞凡F7")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_{action2_id}_step2", "1")
+            redis_client.delete(f"{device_id}_{action2_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        action2_id_mem = redis_client.get(device_id)
+        step2 = redis_client.get(f"{device_id}_{perform_action_id}_step2")
+        if step2 is not None and int(step2) == 1 and action2_id_mem is not None and int(perform_action_id) == int(
+                action2_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(action2_id_mem))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/g9e")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_{action3_id}_step3", "1")
+            redis_client.delete(f"{device_id}_{action3_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        action3_id_mem = redis_client.get(device_id)
+        step3 = redis_client.get(f"{device_id}_{perform_action_id}_step3")
+        if step3 is not None and int(step3) == 1 and action3_id_mem is not None and int(perform_action_id) == int(
+                action3_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(action3_id_mem))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击tab->口碑
+            """
+            action4_dict = single_click_by_text(action4_id, text="口碑")
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_{action4_id}_step4", "1")
+            redis_client.delete(f"{device_id}_{action4_id}_step3")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        action4_id_mem = redis_client.get(device_id)
+        step4 = redis_client.get(f"{device_id}_{perform_action_id}_step4")
+
+        if step4 is not None and int(step4) == 1 and action4_id_mem is not None and int(perform_action_id) == int(
+                action4_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(action4_id_mem))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击tab->车友圈
+            """
+            action5_dict = single_click_by_text(action5_id, text="车友圈")
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_{action5_id}_step5", "1")
+            redis_client.delete(f"{device_id}_{action5_id}_step4")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        action5_id_mem = redis_client.get(device_id)
+        step5 = redis_client.get(f"{device_id}_{perform_action_id}_step5")
+
+        if step5 is not None and int(step5) == 1 and action5_id_mem is not None and int(perform_action_id) == int(
+                action5_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(action5_id_mem))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            点击tab->用户
+            """
+            action6_dict = single_click_by_text(action6_id, text="用户")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_{action6_id}_step6", "1")
+            redis_client.delete(f"{device_id}_{action6_id}_step5")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        # 判断是否有搜索结果
+        action6_id_mem = redis_client.get(device_id)
+        step6 = redis_client.get(f"{device_id}_{perform_action_id}_step6")
+
+        if step6 is not None and int(step6) == 1 and action6_id_mem is not None and int(perform_action_id) == int(
+                action6_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(action6_id_mem))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            点击搜素出来的博主
+            """
+            action7_dict = single_click_by_text(action7_id, text="飞凡F7")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_{action7_id}_step7", "1")
+            redis_client.delete(f"{device_id}_{action7_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        # 存在搜索结果
+        action7_id_mem = redis_client.get(device_id)
+        step7 = redis_client.get(f"{device_id}_{perform_action_id}_step7")
+
+        if step7 is not None and int(step7) == 1 and action7_id_mem is not None and int(perform_action_id) == int(
+                action7_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(action7_id_mem))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            存在搜索结果,已经跳转到了用户中心首页
+            开始匹配文章
+            """
+            action8_dict = get_content_by_control(action8_id, control_id="android.widget.TextView",
+                                                  title="北京福7飞凡车队正式成立啦")
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_{action8_id}_step8", "1")
+            redis_client.delete(f"{device_id}_{action8_id}_step7")
+
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_id)
+
+            return action8_dict
+
+        # 文章匹配,点击评论
+        action8_id_mem = redis_client.get(device_id)
+        step8 = redis_client.get(f"{device_id}_{perform_action_id}_step8")
+        if step8 is not None and int(step8) == 1 and action8_id_mem is not None and int(perform_action_id) == int(
+                action8_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(action8_id_mem))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击评论
+            """
+            loggerKit.info("action9_id:{0}, 文章匹配,点击评论", action9_id)
+            action9_dict = single_click_by_control(action9_id, control_id="com.ss.android.auto:id/ini")
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_{action9_id}_step9", "1")
+            redis_client.delete(f"{device_id}_{action9_id}_step8")
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_id)
+
+            return action9_dict
+
+        # 文章匹配,点击输入框
+        action9_id_mem = redis_client.get(device_id)
+        step9 = redis_client.get(f"{device_id}_{perform_action_id}_step9")
+        if step9 is not None and int(step9) == 1 and action9_id_mem is not None and int(perform_action_id) == int(
+                action9_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(action9_id_mem))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框
+            """
+            loggerKit.info("action10_id:{0}, 文章匹配,点击输入框", action10_id)
+            action10_dict = single_click_by_control(action10_id, control_id="com.ss.android.auto:id/inv")
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_{action10_id}_step10", "1")
+            redis_client.delete(f"{device_id}_{action10_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        # 文章匹配,点击输入框,返回回复内容
+        action10_id_mem = redis_client.get(device_id)
+        step10 = redis_client.get(f"{device_id}_{perform_action_id}_step10")
+        if step10 is not None and int(step10) == 1 and action10_id_mem is not None and int(perform_action_id) == int(
+                action10_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(action10_id_mem))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            loggerKit.info("action11_id:{0}, 文章title:{1}, 回复内容:{2}")
+            action11_dict = send_text_by_control(action11_id, control_id="com.ss.android.auto:id/dcz", content="不错👍")
+
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_{action11_id}_step11", "1")
+            redis_client.delete(f"{device_id}_{action11_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+            return action11_dict
+
+        # 停止app
+        action11_id_mem = redis_client.get(device_id)
+        step11 = redis_client.get(f"{device_id}_{perform_action_id}_step11")
+        if step11 is not None and int(step11) == 1 and action11_id_mem is not None and int(perform_action_id) == int(
+                action11_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(action11_id_mem))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action12_dict = stop_app(action12_id, target_version="7.9.0")
+
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_{action12_id}_step12", "1")
+            redis_client.delete(f"{device_id}_{action12_id}_step11")
+
+            # 批量模糊删除keys
+            keys = redis_client.match_pattern_prefix(device_id)
+            if len(keys) > 0:
+                # 需要判断是否有匹配的值, 没有的话会报错
+                for key in keys:
+                    redis_client.delete(key)
+                loggerKit.info(f"clear {device_id} keys success...")
+            else:
+                loggerKit.info(f"{device_id} keys none ...")
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 领取任务接口
+# @route(app, '/api/action_V2', methods=['POST'])
+def do_action_v2():
+    """
+    :param deviceID [必填]设备的ID
+    :param performActionId [非必填]前一条指令的ID
+    :param performActionResult success(指令成功) or failure(指令失败) or picNotFound(针对查找图片场景 图片未识别到) or stop(指令终止) or eleNotFound(针对元素 未获取到)
+    :param recognizedResult 你好,我是你根据控件Id查到的text内容
+    领取任务接口
+    :return: 指令json串
+    """
+
+    request_data = request.json
+    loggerKit.info('请求信息:{0}'.format(request_data))
+
+    device_id = request_data.get("deviceID")
+
+    if device_id is None:
+        loggerKit.info('请求信息异常:{0}'.format(request_data))
+
+        return_dict = {
+            "data": "",
+            "code": -1,
+            "message": "fail, request device_id is null"
+        }
+
+        return return_dict
+
+    # 缓存中获取到对应的任务信息
+    task_response = redis_client.get(device_id + "_task_json")
+
+    if task_response is not None:
+        data = json.loads(task_response)
+
+        task = task_dict(str(data.get('taskId')), data.get('mediaChannel'), data.get('taskType'), data.get('taskDesc'),
+                         data.get('actionType'),
+                         data.get('resourceName'), data.get('subResourceName'), data.get('executeRobotAccount'),
+                         data.get('executeRobotName'), data.get('deviceId'), data.get('content'),
+                         data.get('answerType'),
+                         data.get('materialUri'),
+                         data.get('taskSubType'), data.get('taskSequenceId'), None, None, None, request_data,
+                         data.get('taskKey'))
+        handler = rpa_handler().set_processor(task)
+
+        return handler.process_rpa(task)
+
+
+# 领取任务接口
+# @route(app, '/api/action', methods=['POST'])
+def do_action_():
+    """
+    :param deviceID [必填]设备的ID
+    :param performActionId [非必填]前一条指令的ID
+    :param performActionResult success or failure
+    :param recognizedResult 你好,我是你根据控件Id查到的text内容
+    领取任务接口
+    :return: 指令json串
+    """
+
+    data = request.json
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if device_id is None:
+        loggerKit.info('请求信息异常:{0}'.format(data))
+
+        return_dict = {
+            "data": "",
+            "code": -1,
+            "message": "fail, request device_id is null"
+        }
+
+        return return_dict
+
+    # 获取到对应策略
+    action0_id_mem = redis_client.get(device_id)
+
+    # action0_id = int(round(time.time() * 1000))
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            return return_dict
+
+        action0_id_mem = redis_client.get(device_id)
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and action0_id_mem is not None and int(perform_action_id) == int(
+                action0_id_mem) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            懂车帝APP首页点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_version="7.9.0",
+                                                   control_id="com.ss.android.auto:id/c9c")
+
+            redis_client.set(device_id, action1_id)
+            redis_client.set(f"{device_id}_{action1_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        action1_id_mem = redis_client.get(device_id)
+        step1 = redis_client.get(f"{device_id}_{perform_action_id}_step1")
+        if step1 is not None and int(step1) == 1 and action1_id_mem is not None and int(perform_action_id) == int(
+                action1_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(action1_id_mem))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在搜索框输入关键词
+            """
+            action2_dict = send_text_by_control(action2_id, control_id="com.ss.android.auto:id/h5q", content="飞凡F7")
+
+            redis_client.set(device_id, action2_id)
+            redis_client.set(f"{device_id}_{action2_id}_step2", "1")
+            redis_client.delete(f"{device_id}_{action2_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        action2_id_mem = redis_client.get(device_id)
+        step2 = redis_client.get(f"{device_id}_{perform_action_id}_step2")
+        if step2 is not None and int(step2) == 1 and action2_id_mem is not None and int(perform_action_id) == int(
+                action2_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(action2_id_mem))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/g9e")
+
+            redis_client.set(device_id, action3_id)
+            redis_client.set(f"{device_id}_{action3_id}_step3", "1")
+            redis_client.delete(f"{device_id}_{action3_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        action3_id_mem = redis_client.get(device_id)
+        step3 = redis_client.get(f"{device_id}_{perform_action_id}_step3")
+        if step3 is not None and int(step3) == 1 and action3_id_mem is not None and int(perform_action_id) == int(
+                action3_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(action3_id_mem))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击tab->口碑
+            """
+            action4_dict = single_click_by_text(action4_id, text="二手车")
+
+            redis_client.set(device_id, action4_id)
+            redis_client.set(f"{device_id}_{action4_id}_step4", "1")
+            redis_client.delete(f"{device_id}_{action4_id}_step3")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        action4_id_mem = redis_client.get(device_id)
+        step4 = redis_client.get(f"{device_id}_{perform_action_id}_step4")
+
+        if step4 is not None and int(step4) == 1 and action4_id_mem is not None and int(perform_action_id) == int(
+                action4_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(action4_id_mem))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击tab->车友圈
+            """
+            action5_dict = single_click_by_text(action5_id, text="车友圈")
+
+            redis_client.set(device_id, action5_id)
+            redis_client.set(f"{device_id}_{action5_id}_step5", "1")
+            redis_client.delete(f"{device_id}_{action5_id}_step4")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        action5_id_mem = redis_client.get(device_id)
+        step5 = redis_client.get(f"{device_id}_{perform_action_id}_step5")
+
+        if step5 is not None and int(step5) == 1 and action5_id_mem is not None and int(perform_action_id) == int(
+                action5_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(action5_id_mem))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            点击tab->用户
+            """
+            action6_dict = single_click_by_text(action6_id, text="用户")
+
+            redis_client.set(device_id, action6_id)
+            redis_client.set(f"{device_id}_{action6_id}_step6", "1")
+            redis_client.delete(f"{device_id}_{action6_id}_step5")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        # 判断是否有搜索结果
+        action6_id_mem = redis_client.get(device_id)
+        step6 = redis_client.get(f"{device_id}_{perform_action_id}_step6")
+
+        if step6 is not None and int(step6) == 1 and action6_id_mem is not None and int(perform_action_id) == int(
+                action6_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(action6_id_mem))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            点击搜素出来的博主
+            """
+            action7_dict = single_click_by_text(action7_id, text="飞凡F7")
+
+            redis_client.set(device_id, action7_id)
+            redis_client.set(f"{device_id}_{action7_id}_step7", "1")
+            redis_client.delete(f"{device_id}_{action7_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        # 存在搜索结果
+        action7_id_mem = redis_client.get(device_id)
+        step7 = redis_client.get(f"{device_id}_{perform_action_id}_step7")
+
+        if step7 is not None and int(step7) == 1 and action7_id_mem is not None and int(perform_action_id) == int(
+                action7_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(action7_id_mem))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            存在搜索结果,已经跳转到了用户中心首页
+            开始匹配文章
+            """
+            action8_dict = get_content_by_control(action8_id, control_id="android.widget.TextView",
+                                                  title="北京福7飞凡车队正式成立啦")
+
+            redis_client.set(device_id, action8_id)
+            redis_client.set(f"{device_id}_{action8_id}_step8", "1")
+            redis_client.delete(f"{device_id}_{action8_id}_step7")
+
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_id)
+
+            return action8_dict
+
+        # 文章评论,点击铅笔父节点
+        action8_id_mem = redis_client.get(device_id)
+        step8 = redis_client.get(f"{device_id}_{perform_action_id}_step8")
+        if step8 is not None and int(step8) == 1 and action8_id_mem is not None and int(perform_action_id) == int(
+                action8_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(action8_id_mem))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击评论
+            """
+            loggerKit.info("action9_id:{0}, 文章评论,点击铅笔父节点", action9_id)
+            action9_dict = single_click_by_control(action9_id, control_id="com.ss.android.auto:id/d98")
+
+            redis_client.set(device_id, action9_id)
+            redis_client.set(f"{device_id}_{action9_id}_step9", "1")
+            redis_client.delete(f"{device_id}_{action9_id}_step8")
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_id)
+
+            return action9_dict
+
+        """
+        文章匹配,点击输入框,返回回复内容
+        """
+        action9_id_mem = redis_client.get(device_id)
+        step9 = redis_client.get(f"{device_id}_{perform_action_id}_step9")
+        if step9 is not None and int(step9) == 1 and action9_id_mem is not None and int(perform_action_id) == int(
+                action9_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(action9_id_mem))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            loggerKit.info("action10_id:{0}, 文章title:{1}, 回复内容:{2}")
+            action11_dict = send_text_by_control(action10_id, control_id="com.ss.android.auto:id/byd", content="不错👍")
+
+            redis_client.set(device_id, action10_id)
+            redis_client.set(f"{device_id}_{action10_id}_step10", "1")
+            redis_client.delete(f"{device_id}_{action10_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+            return action11_dict
+
+        """
+        点击发送按钮
+        """
+        action10_id_mem = redis_client.get(device_id)
+        step10 = redis_client.get(f"{device_id}_{perform_action_id}_step10")
+        if step10 is not None and int(step10) == 1 and action10_id_mem is not None and int(perform_action_id) == int(
+                action10_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(action10_id_mem))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            action11_dict = single_click_by_control(action11_id, control_id="com.ss.android.auto:id/jxq")
+
+            redis_client.set(device_id, action11_id)
+            redis_client.set(f"{device_id}_{action11_id}_step11", "1")
+            redis_client.delete(f"{device_id}_{action11_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+            return action11_dict
+
+        """
+        停止app
+        """
+        action11_id_mem = redis_client.get(device_id)
+        step11 = redis_client.get(f"{device_id}_{perform_action_id}_step11")
+        if step11 is not None and int(step11) == 1 and action11_id_mem is not None and int(perform_action_id) == int(
+                action11_id_mem) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(action11_id_mem))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action12_dict = stop_app(action12_id, target_version="7.9.0")
+
+            redis_client.set(device_id, action12_id)
+            redis_client.set(f"{device_id}_{action12_id}_step12", "1")
+            redis_client.delete(f"{device_id}_{action12_id}_step11")
+
+            # 批量模糊删除keys
+            keys = redis_client.match_pattern_prefix(device_id)
+            if len(keys) > 0:
+                # 需要判断是否有匹配的值, 没有的话会报错
+                for key in keys:
+                    redis_client.delete(key)
+                loggerKit.info(f"clear {device_id} keys success...")
+            else:
+                loggerKit.info(f"{device_id} keys none ...")
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id, action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 定时任务
+def scheduler_start():
+    rpa_scheduler = BackgroundScheduler()
+
+    # 定时拉取任务
+    rpa_scheduler.add_job(auto_pull_task, 'interval', seconds=1, max_instances=60)
+    # 定时上报设备状态
+    # rpa_scheduler.add_job(auto_synchronization_devices_status_func, 'interval', seconds=60, max_instances=2)
+    rpa_scheduler.start()
+
+
+@route(app, '/api/action', methods=['POST'])
+def do_action():
+    """
+    入参:
+    :param taskId
+    :param taskType
+    :param subTaskType
+    :param requestId
+    :param scriptName
+    :param deviceID [必填]设备的ID
+    :param performActionId [非必填]前一条指令的ID
+    :param performActionResult success(指令成功) or failure(指令失败) or picNotFound(针对查找图片场景 图片未识别到) or stop(指令终止) or eleNotFound(针对元素 未获取到)
+    :param recognizedResult 你好,我是你根据控件Id查到的text内容
+    领取任务接口
+    :return: 指令json串
+    """
+
+    request_data = request.json
+    loggerKit.info('请求信息:{0}'.format(request_data))
+
+    # device_work(device_id)
+
+    # 缓存中获取到对应的任务信息
+    # task_response = redis_client.get(device_id + "_task_json")
+
+    """
+    根据任务类型、子任务类型匹配不同的执行脚本
+    """
+    if request_data is not None:
+
+        device_id = request_data.get("deviceID")
+        # task_id = request_data.get("taskID")
+        # task_type = request_data.get("taskType")
+        # sub_task_type = request_data.get("subTaskType")
+        data = request_data.get("data")
+
+        if device_id is None or data is None:
+            loggerKit.info('请求信息异常:{0}'.format(request_data))
+
+            return_dict = {
+                "data": "",
+                "code": -1,
+                "message": "fail, request device_id or task_pull_data is none"
+            }
+
+            return return_dict
+
+        task = task_dict(int(data.get('taskId')), data.get('mediaChannel'), data.get('taskType'), data.get('taskDesc'),
+                         data.get('actionType'),
+                         data.get('resourceName'), data.get('subResourceName'), data.get('executeRobotAccount'),
+                         data.get('executeRobotName'), data.get('deviceId'), data.get('content'),
+                         data.get('answerType'),
+                         data.get('materialUri'),
+                         data.get('taskSubType'), data.get('taskSequenceId'), None, None, None, request_data,data.get('taskKey'))
+        handler = rpa_atom_handler().set_processor(task)
+
+        return handler.process_rpa(task)
+
+
+if __name__ == '__main__':
+    app.json_encoder = JSONEncoder
+    # scheduler_start()
+
+    app.run(debug=True, host='0.0.0.0', port=80)
+    print(f'API服务启动成功')

BIN
box_advise.png


BIN
boxes.png


+ 21 - 0
config.py

@@ -0,0 +1,21 @@
+# config.py
+USERNAME = 'root'
+PASSWORD = 'root'
+HOSTNAME = "127.0.0.1"
+PORT = '3306'
+DATABASE = 'xag'
+
+DIALECT = 'mysql'
+DRIVER = 'pymysql'
+
+# 连接数据的URI
+DB_URI = "{}+{}://{}:{}@{}:{}/{}?charset=utf8".format(DIALECT, DRIVER, USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
+
+SQLALCHEMY_DATABASE_URI = DB_URI
+
+SQLALCHEMY_TRACK_MODIFICATIONS = True
+
+SWAGGER_TITLE = "API"
+SWAGGER_DESC = "API接口"
+# 地址,必须带上端口号
+SWAGGER_HOST = "localhost:5000"

+ 36 - 0
config.yaml

@@ -0,0 +1,36 @@
+redis:
+  host: 'r-uf6e82u4wteud6wpwz.redis.rds.aliyuncs.com'
+  port: 6379
+  password: 'Saic1234'
+  db: 0
+  timeout: 600
+
+bmp-cp:
+  net_domain: 'http://api-qa.risingauto.com/'
+  extern_domain: 'http://api-qa.risingauto.com/'
+  get_task_url: 'api/bmp-cp/task/pullTask'
+  get_task_url_v2: 'api/bmp-cp/task/pullTaskV2'
+  get_task_url_v3: 'api/bmp-cp/task/pullTaskV3'
+  task_callback_url: 'api/bmp-cp/task/callBackTaskV4'
+  device_discovery_url: 'api/bmp-cp/computerDevice/deviceDiscovery'
+  server_heart_url: 'api/bmp-cp/computerDevice/severHeartBeatStatusDetection'
+  status_sync_url: 'api/bmp-cp/device/multipleDeviceStatusMaintenance'
+  event_url: 'https://ty-datakit.risingauto.com:10100/event?token=0a248454-3cea-42de-9d9c-ade3e76b1d9e'
+  robot_able_comment_url: 'api/bmp-cp/content/robotAbleComment'
+  robot_comment_url: 'api/bmp-cp/content/robotComment'
+  get_comment_url: 'api/bmp-cp/content/comment'
+#  post_upload_device_status_url: 'api/bmp-cp/user/device/upload'
+  get_port_serial_url: 'api/bmp-cp/port/getPortSerial'
+  get_commented_list_url: 'api/bmp-cp/interactive_behavior/comment/list'
+  post_ai_gc_url: 'http://47.116.62.124:3000/comment-saic-pv'
+  post_ai_gc_url_dy: 'http://47.116.62.124:3000/comment-saic-pv-dy'
+  post_ai_gc_library_url: 'http://47.116.62.124:3000/comment-library'
+  remote_server_addr: '47.92.249.14'
+  port_list_port_serial: 'api/bmp-cp/port/listPortSerial'
+  user_operate_add_url: 'api/bmp-cp/user/operate/add'
+  user_operate_is_like_url: 'api/bmp-cp/user/operate/is-like'
+
+bmp-content-center:
+  comment_direction_url: 'api/content-center/comments/aigc/detect'
+  comment_generate_url: 'api/content-center/comments/aigc/generate'
+  comment_local_url: 'api/content-center/comments//aigc/local'

+ 694 - 0
cubic/cubic_simple_action.py

@@ -0,0 +1,694 @@
+"""
+汽车之家官媒互动
+"""
+import random
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, send_text_by_control, swipe_screen, \
+    stop_app, start_app, get_content_by_control, single_click_by_control_exact
+from tools import redis_client, loggerKit
+import json
+import httpx
+import threading
+import yaml
+import requests
+from urllib.parse import quote
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = extern_domain + config['bmp-content-center']['comment_local_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+def cubic_simple_action(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    # title = inner_data.get("resourceName")
+    # author = inner_data.get("subResourceName")
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    title = extension_info['title']
+    author = extension_info['author']
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令被用户终止', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:搜索目标帖子
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="cubic",
+                                                   target_version="11.59.5",
+                                                   package_name="com.cubic.autohome",
+                                                   control_id="com.cubic.autohome:id/search_bar_vertical_banner_view",
+                                                   item_index=4)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            发送第2条指令
+            在搜索框输入作者
+            """
+            action2_dict = send_text_by_control(action2_id,
+                                                control_id="com.autohome.plugin.search:id"
+                                                           "/fragment_search_keyword_autocompleteview",
+                                                content=f'{author}',
+                                                target_app="cubic",
+                                                target_version="11.59.5",
+                                                package_name="com.cubic.autohome")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            发送第3条指令
+            点击搜索
+            """
+            action3_dict = single_click_by_control(action3_id,
+                                                   target_app="cubic",
+                                                   target_version="11.59.5",
+                                                   package_name="com.cubic.autohome",
+                                                   control_id="com.autohome.plugin.search:id"
+                                                              "/fragment_search_action_textview")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            发送第4条指令
+            通过文本点击
+            """
+            action4_dict = get_content_by_control(action4_id, title=f'{author}',
+                                                  target_app="cubic",
+                                                  target_version="11.59.5",
+                                                  package_name="com.cubic.autohome")
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action5_id = int(round(time.time() * 1000))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            发送第5条指令
+            通过文本点击
+            """
+            action5_dict = get_content_by_control(action5_id,
+                                                  title=f"{title}",
+                                                  target_app="cubic",
+                                                  target_version="11.59.5",
+                                                  package_name="com.cubic.autohome")
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action5_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            点击视频/帖子点赞
+            """
+            action7_dict = single_click_by_control(action7_id,
+                                                   target_app="cubic",
+                                                   target_version="11.59.5",
+                                                   package_name="com.cubic.autohome",
+                                                   control_id="com.autohome.main.article:id/bottom_space",
+                                                   control_ids="com.autohome.main.club:id/fl_praise_root,com.autohome.main.article:id/bottom_space,com.autohome.main.article:id/moments_like",
+                                                   item_index=0,
+                                                   sub_item_index=2)
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            action8_id = int(round(time.time() * 1000))
+            """
+            点击视频/帖子 收藏
+            """
+            action8_dict = single_click_by_control(action8_id,
+                                                   target_app="cubic",
+                                                   target_version="11.59.5",
+                                                   package_name="com.cubic.autohome",
+                                                   control_id="com.autohome.main.article:id/bottom_space",
+                                                   control_ids="com.autohome.main.club:id/fl_collect_root,com.autohome.main.article:id/bottom_space,com.autohome.main.article:id/moments_favorite",
+                                                   item_index=0,
+                                                   sub_item_index=3)
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            action8_id = int(round(time.time() * 1000))
+            """
+            点击视频的评论框 进入评论区
+            """
+            action8_dict = single_click_by_control(action8_id,
+                                                   target_app="cubic",
+                                                   target_version="11.59.5",
+                                                   package_name="com.cubic.autohome",
+                                                   control_id="com.autohome.main.article:id/moments_comment")
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action8_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            """
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + title, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, title, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, title, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "mode": "1",
+                "title": title,
+                "platform": "autohome",
+                "comment": existing_comment
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, title, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action10_dict = single_click_by_control(action10_id,
+                                                    target_app="cubic", target_version="11.59.5",
+                                                    package_name="com.cubic.autohome",
+                                                    control_ids="com.autohome.main.club:id/topic_page_publish_entrance,com.autohome.main.article:id/article_new_bottombar_dysc,com.autohome.main.article:id/bottom_space",
+                                                    timeout=5,
+                                                    item_index=0,
+                                                    sub_item_index=0)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, title, request_data,
+                               response)
+
+                redis_client.set(device_id + "autoHome" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            response_body = json.loads(response.text)
+
+            reply_data = response_body.get('data')
+
+            reply = reply_data.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, title, request_data,
+                               response)
+
+                redis_client.set(device_id + "autoHome" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "autoHome" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action12_id_mem:{1}", device_id, int(last_action_id))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "autoHome" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action12_dict = send_text_by_control(action12_id, target_app="cubic",
+                                                 target_version="11.59.5",
+                                                 package_name="com.cubic.autohome",
+                                                 control_id="com.cubic.autohome:id/input_edit",
+                                                 content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            content_result = content_detail(title, title, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            发送评论
+            """
+            action12_dict = single_click_by_control_exact(action13_id, target_app="cubic",
+                                                          target_version="11.59.5",
+                                                          package_name="com.cubic.autohome",
+                                                          control_id="com.cubic.autohome:id/input_btn_send", timeout=5)
+            redis_client.set(device_id + "operate", action13_id)
+            redis_client.set(f"{device_id}_step13", "1")
+            redis_client.delete(f"{device_id}_step12")
+
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+        step13 = redis_client.get(f"{device_id}_step13")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            发送第14条指令
+            随机滑动
+            """
+            random_scale = random.randint(1, 3)
+            action6_dict = swipe_screen(action6_id, scale=random_scale, timeout=30,
+                                        target_app="cubic",
+                                        target_version="11.59.5",
+                                        package_name="com.cubic.autohome")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step14", "1")
+            redis_client.delete(f"{device_id}_step13")
+
+            return action6_dict
+
+        step13 = redis_client.get(f"{device_id}_step14")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action9_id = int(round(time.time() * 1000))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+                return return_dict
+            """
+            发送第15条指令
+            关闭app
+            """
+            action9_dict = stop_app(action9_id, target_app="cubic",
+                                    target_version="11.59.5",
+                                    package_name="com.cubic.autohome")
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_dict)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            # 回调任务中心修改任务状态
+            call_back_task_reply(None, None, task_id, device_id, 1, None, reply_json)
+            del_key_vague(device_id)
+
+            return action9_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+        action0_dict = start_app(action0_id, target_app="cubic",
+                                 target_version="11.59.5",
+                                 package_name="com.cubic.autohome")
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+def call_back_task_reply(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 6 - 0
db.toml

@@ -0,0 +1,6 @@
+[database]
+host = 'rm-uf6d37h6899a9mrqw.mysql.rds.aliyuncs.com'
+user = 'rauto'
+port = 3306
+password = 'Saic6666'
+database = 'db_bmp_cp'

BIN
dongchedi/__pycache__/dongchedi_sign.cpython-311.pyc


BIN
dongchedi/__pycache__/random_browse.cpython-311.pyc


+ 243 - 0
dongchedi/atom-task/confirmed_browse.py

@@ -0,0 +1,243 @@
+"""
+浏览首页内容20篇
+"""
+import random
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import swipe_screen, start_app, single_click_by_control, back_last_page, stop_app
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+
+
+def confirmed_browse(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    title = inner_data.get("resourceName")
+    author = inner_data.get("subResourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+            return return_dict
+
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            首页下拉刷新
+            """
+            action1_dict = swipe_screen(action1_id, scale=0.5, timeout=30, direction="down")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在首页随机滑动2~3屏
+            """
+            random_scale = random.randint(1, 3)
+            action2_dict = swipe_screen(action2_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        """
+        进入帖子
+        """
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击进入帖子
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        """
+        在帖子中随机滑动1~3屏
+        """
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            在帖子中随机滑动1~3屏
+            """
+            random_scale = random.randint(1, 3)
+            action4_dict = swipe_screen(action4_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and (
+                last_action_id is not None and int(perform_action_id) == int(last_action_id)
+                and perform_action_result == "success"):
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            退出当前帖子
+            """
+            action5_dict = back_last_page(action5_id)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+
+            # 完成看完1篇文章数
+            browsed_count = redis_client.incr(device_id + "browsed_count", 1)
+            if int(browsed_count) == 20:
+                """
+                发送第6条指令
+                关闭app
+                """
+                action6_dict = stop_app(action6_id, target_version="7.9.0")
+                del_key_vague(device_id)
+                loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, None)
+
+                return action6_dict
+
+            else:
+
+                # 继续看下一篇帖子
+                action2_id = int(round(time.time() * 1000))
+                """
+                发送第2条指令
+                在首页随机滑动2~3屏
+                """
+                random_scale = random.randint(1, 3)
+                action2_dict = swipe_screen(action2_id, scale=random_scale, timeout=30)
+
+                redis_client.set(device_id + "operate", action2_id)
+                redis_client.set(f"{device_id}_step2", "1")
+                redis_client.delete(f"{device_id}_step1")
+                loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+                return action2_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 176 - 0
dongchedi/atom-task/platform_sign.py

@@ -0,0 +1,176 @@
+"""
+平台用户签到
+"""
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, click_pic, stop_app, start_app
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+
+
+def platform_sign(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击进入我的页面
+            """
+            action1_dict = single_click_by_control(action1_id, target_app="dongchedi", target_version="7.9.0",
+                                                   package_name="com.ss.android.auto",
+                                                   control_id="android:id/tabs", timeout=5, sleep_time=5,
+                                                   item_index=4)
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击进入签到元素
+            """
+            action2_dict = single_click_by_control(action2_id, target_app="dongchedi", target_version="7.9.0",
+                                                   package_name="com.ss.android.auto",
+                                                   control_id="com.ss.android.auto:id/a6l",
+                                                   timeout=5, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        dongchedi_sign_path = 'dongchedi/pic/dongchedi_sign.png'
+
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击签到
+            """
+            dongchedi_sign_base64 = pic_to_base64(dongchedi_sign_path)
+
+            action3_dict = click_pic(action3_id, target_app="dongchedi", target_version="7.9.0",
+                                     package_name="com.ss.android.auto",
+                                     pic_base64=dongchedi_sign_base64)
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)
+                                                      and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            关闭app
+            """
+            action4_dict = stop_app(action4_id, target_version="7.9.0", target_app="dongchedi", timeout=5, sleep_time=3)
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_dict)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action4_dict
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 243 - 0
dongchedi/atom-task/random_browse.py

@@ -0,0 +1,243 @@
+"""
+随机浏览若干篇文章(10篇)
+"""
+import random
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import swipe_screen, single_click_by_control, back_last_page, stop_app, start_app
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+
+
+def random_browse(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    title = inner_data.get("resourceName")
+    author = inner_data.get("subResourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+            return return_dict
+
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            首页下拉刷新
+            """
+            action1_dict = swipe_screen(action1_id, scale=0.5, timeout=30, direction="down")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在首页随机滑动2~3屏
+            """
+            random_scale = random.randint(1, 3)
+            action2_dict = swipe_screen(action2_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        """
+        进入帖子
+        """
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击进入帖子
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        """
+        在帖子中随机滑动1~3屏
+        """
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            在帖子中随机滑动1~3屏
+            """
+            random_scale = random.randint(1, 3)
+            action4_dict = swipe_screen(action4_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and (
+                last_action_id is not None and int(perform_action_id) == int(last_action_id)
+                and perform_action_result == "success"):
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            退出当前帖子
+            """
+            action5_dict = back_last_page(action5_id)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+
+            # 完成看完1篇文章数
+            browsed_count = redis_client.incr(device_id + "browsed_count", 1)
+            if int(browsed_count) == 10:
+                """
+                发送第6条指令
+                关闭app
+                """
+                action6_dict = stop_app(action6_id, target_version="7.9.0")
+                del_key_vague(device_id)
+                loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, None)
+
+                return action6_dict
+
+            else:
+
+                # 继续看下一篇帖子
+                action2_id = int(round(time.time() * 1000))
+                """
+                发送第2条指令
+                在首页随机滑动2~3屏
+                """
+                random_scale = random.randint(1, 3)
+                action2_dict = swipe_screen(action2_id, scale=random_scale, timeout=30)
+
+                redis_client.set(device_id + "operate", action2_id)
+                redis_client.set(f"{device_id}_step2", "1")
+                redis_client.delete(f"{device_id}_step1")
+                loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+                return action2_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 401 - 0
dongchedi/atom-task/search_article_comment.py

@@ -0,0 +1,401 @@
+"""
+搜索文章、评论
+"""
+import json
+import time
+from urllib.parse import quote
+
+import httpx
+import yaml
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import stop_app, start_app, send_text_by_control, single_click_by_control, \
+    single_click_by_text, get_content_by_control
+from task.task_job import callback_task
+from tools import redis_client, loggerKit
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+"""
+搜索文章、评论
+"""
+
+
+def search_article_comment(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    title = inner_data.get("resourceName")
+    author = inner_data.get("subResourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+            return return_dict
+
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            懂车帝APP首页点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_version="7.9.0",
+                                                   control_id="com.ss.android.auto:id/c9c")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在搜索框输入关键词
+            """
+            action2_dict = send_text_by_control(action2_id, control_id="com.ss.android.auto:id/h5q",
+                                                content=f'{author}')
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/g9e")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击tab->口碑
+            """
+            action4_dict = single_click_by_text(action4_id, text="二手车")
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击tab->车友圈
+            """
+            action5_dict = single_click_by_text(action5_id, text="车友圈")
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            点击tab->用户
+            """
+            action6_dict = single_click_by_text(action6_id, text="用户")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            return action6_dict
+
+        # 判断是否有搜索结果
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            """
+            点击搜素出来的博主
+            """
+            action7_dict = single_click_by_text(action7_id, text=f"{author}")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action7_dict
+
+        # 存在搜索结果
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            存在搜索结果,已经跳转到了用户中心首页
+            开始匹配文章
+            """
+            action8_dict = get_content_by_control(action8_id, control_id="android.widget.TextView",
+                                                  title=f'{title}')
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 文章评论,点击铅笔父节点
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击评论
+            """
+            loggerKit.info("action9_id:{0}, 文章评论,点击铅笔父节点", action9_id)
+            action9_dict = single_click_by_control(action9_id, control_id="com.ss.android.auto:id/d98")
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        """
+        文章匹配,点击输入框,返回回复内容
+        """
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action10_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            loggerKit.info("action10_id:{0}, 文章title:{1}, 回复内容:{2}")
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + title, timeout=120)
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+            # 评论集合
+            comment_list = comment_body.get('data')
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, title, comment_list)
+            existing_comment = ''
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+                existing_comment = '||'.join(comment_list)
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, title, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "templateId": "dcd_comment",
+                "content": title,
+                "existingComment": existing_comment
+            }
+
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+            loggerKit.info("任务id:{0}获取AIGC评论结果:{1}", task_id, response)
+
+            reply_content = "不错👍"
+            if response.is_success:
+                response_body = json.loads(response.text)
+                data = response_body.get('data')
+                reply_text = data.get('answer')
+                reply_text_json = json.loads(reply_text)
+                reply_content = reply_text_json.get('comment')
+                if len(reply_content) == 0:
+                    reply_content = "赞👍"
+
+            content_result = content_detail(title, author, reply_content, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            action10_dict = send_text_by_control(action10_id,
+                                                 control_id="com.ss.android.auto:id/byd",
+                                                 content=f'{reply_content}')
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            return action10_dict
+
+        """
+        点击发送按钮
+        """
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action11_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            action11_dict = single_click_by_control(action11_id, control_id="com.ss.android.auto:id/jxq")
+
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            return action11_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            关闭app
+            """
+            action12_dict = stop_app(action12_id, target_version="7.9.0")
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_id)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action12_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 416 - 0
dongchedi/atom-task/search_article_like_comment.py

@@ -0,0 +1,416 @@
+"""
+搜索文章、点赞、评论
+"""
+import json
+import time
+from urllib.parse import quote
+
+import httpx
+import yaml
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import swipe_screen, single_click_by_control, single_click_by_text, send_text_by_control, \
+    get_content_by_control, start_app, stop_app
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+def search_article_like_comment(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    title = inner_data.get("resourceName")
+    author = inner_data.get("subResourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+            return return_dict
+
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            懂车帝APP首页点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_version="7.9.0",
+                                                   control_id="com.ss.android.auto:id/c9c")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在搜索框输入关键词
+            """
+            action2_dict = send_text_by_control(action2_id, control_id="com.ss.android.auto:id/h5q",
+                                                content=f'{author}')
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/g9e")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击tab->口碑
+            """
+            action4_dict = single_click_by_text(action4_id, text="二手车")
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击tab->车友圈
+            """
+            action5_dict = single_click_by_text(action5_id, text="车友圈")
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            点击tab->用户
+            """
+            action6_dict = single_click_by_text(action6_id, text="用户")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            return action6_dict
+
+        # 判断是否有搜索结果
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            """
+            点击搜素出来的博主
+            """
+            action7_dict = single_click_by_text(action7_id, text=f"{author}")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action7_dict
+
+        # 存在搜索结果
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            存在搜索结果,已经跳转到了用户中心首页
+            开始匹配文章
+            """
+            action8_dict = get_content_by_control(action8_id, control_id="android.widget.TextView",
+                                                  title=f'{title}')
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 文章评论,点击铅笔父节点
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击评论
+            """
+            loggerKit.info("action9_id:{0}, 文章评论,点击铅笔父节点", action9_id)
+            action9_dict = single_click_by_control(action9_id, control_id="com.ss.android.auto:id/d98")
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        """
+        文章匹配,点击输入框,返回回复内容
+        """
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action10_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            loggerKit.info("action10_id:{0}, 文章title:{1}, 回复内容:{2}")
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + title, timeout=120)
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+            # 评论集合
+            comment_list = comment_body.get('data')
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, title, comment_list)
+            existing_comment = ''
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+                existing_comment = '||'.join(comment_list)
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, title, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "templateId": "dcd_comment",
+                "content": title,
+                "existingComment": existing_comment
+            }
+
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+            loggerKit.info("任务id:{0}获取AIGC评论结果:{1}", task_id, response)
+
+            reply_content = "不错👍"
+            if response.is_success:
+                response_body = json.loads(response.text)
+                data = response_body.get('data')
+                reply_text = data.get('answer')
+                reply_text_json = json.loads(reply_text)
+                reply_content = reply_text_json.get('comment')
+                if len(reply_content) == 0:
+                    reply_content = "赞👍"
+
+            content_result = content_detail(title, author, reply_content, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            action10_dict = send_text_by_control(action10_id,
+                                                 control_id="com.ss.android.auto:id/byd",
+                                                 content=f'{reply_content}')
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            return action10_dict
+
+        """
+        点击发送按钮
+        """
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action11_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            action11_dict = single_click_by_control(action11_id, control_id="com.ss.android.auto:id/jxq")
+
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            return action11_dict
+
+        """
+        点赞
+        """
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action12_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            action12_dict = single_click_by_control(action12_id, control_id="com.ss.android.auto:id/en3")
+
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(
+            last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            关闭app
+            """
+            action13_dict = stop_app(action13_id, target_version="7.9.0")
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action13_id)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action13_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 367 - 0
dongchedi/complete_browse.py

@@ -0,0 +1,367 @@
+"""
+懂车帝
+完整养号
+"""
+import random
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, swipe_screen, single_click_by_text, start_app
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+
+
+def complete_browse(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    inner_data = data.get("data")
+    keyword = inner_data.get("resourceName")
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+            return return_dict
+
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            首页下拉刷新
+            """
+            action1_dict = swipe_screen(action1_id, scale=0.5, timeout=30, direction="down")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在首页随机滑动2~3屏
+            """
+            random_scale = random.randint(1, 3)
+            action2_dict = swipe_screen(action2_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        """
+        点击关键词搜索框
+        """
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击进入帖子
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        """
+        输入关键词
+        """
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            输入关键词
+            """
+            action4_dict = single_click_by_control(action4_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        """
+        点击搜索(关键词)
+        """
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击搜索(关键词)
+            """
+            action5_dict = single_click_by_control(action5_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        """
+        切换到"车友圈"tab
+        """
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            切换到"车友圈"tab
+            """
+            action6_dict = single_click_by_text(action6_id, text="车友圈")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        """
+        点击进圈
+        """
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and (
+                last_action_id is not None and int(perform_action_id) == int(last_action_id)
+                and perform_action_result == "success"):
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第67条指令
+            点击进圈
+            """
+            action7_dict = single_click_by_text(action7_id, text="进圈")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        """
+        随机滑动3-5屏
+        """
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            随机滑动3-5屏
+            """
+            random_scale = random.randint(2, 6)
+            action8_dict = swipe_screen(action8_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action8_id)
+
+            return action8_dict
+
+        """
+        点击帖子
+        """
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)
+                                                      and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            随机滑动点击帖子
+            """
+            action9_dict = single_click_by_control(action9_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action9_id)
+
+            return action9_dict
+
+        """
+        切换到热门帖子
+        """
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)
+                                                      and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            随机滑动点击帖子
+            """
+            action10_dict = single_click_by_control(action10_id, control_id="com.ss.android.auto:id/jum",
+                                                    control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                                "com.ss.android.auto:id/p")
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        """
+        随机滑动屏幕
+        """
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            随机滑动3-5屏
+            """
+            random_scale = random.randint(1, 3)
+            action11_dict = swipe_screen(action11_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        """
+        点击帖子
+        """
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and (last_action_id is not None
+                                                        and int(perform_action_id) == int(last_action_id)
+                                                        and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            随机滑动点击帖子
+            """
+            action12_dict = single_click_by_control(action12_id, control_id="com.ss.android.auto:id/jum",
+                                                    control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                                "com.ss.android.auto:id/p")
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 258 - 0
dongchedi/dongchedi_interaction.py

@@ -0,0 +1,258 @@
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, stop_app, start_app, \
+    single_click_by_text, click_pic, send_text_by_control, get_content_by_control
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+
+"""
+懂车帝策略互动
+"""
+
+
+def dongchedi_interaction(task_id, mobile, data, sub_resource_name):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    keyword = data.get("resourceName")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, mobile, 0, None)
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索按钮
+            """
+            action1_dict = single_click_by_control(action1_id, target_app="dongchedi", target_version="7.9.0",
+                                                   package_name="com.ss.android.auto",
+                                                   control_id="com.ss.android.auto:id/d34", timeout=5, sleep_time=5,
+                                                   item_index=4)
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            搜索框赋值
+            """
+            action2_dict = send_text_by_control(action2_id, target_app="dongchedi", target_version="7.9.0",
+                                                package_name="com.ss.android.auto",
+                                                control_id="com.ss.android.auto:id/h5q",
+                                                content=keyword,
+                                                timeout=5, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="dongchedi", target_version="7.9.0",
+                                                   package_name="com.ss.android.auto",
+                                                   control_id="com.ss.android.auto:id/g9e", timeout=5, sleep_time=5,
+                                                   item_index=4)
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            根据标题名称进行点击
+            """
+            action3_dict = single_click_by_text(action4_id, target_app="dongchedi", target_version="7.9.0",
+                                                package_name="com.ss.android.auto",
+                                                text=keyword, timeout=5, sleep_time=5, similarity=0.8)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            判断有没有点击到标题
+            如果点击到标题中 进行评论点赞收藏操作
+            如果没有点击到标题中 进行搜索用户操作
+            """
+            if perform_action_result == "invalid operation":
+                action5_dict = send_text_by_control(action5_id, target_app="dongchedi", target_version="7.9.0",
+                                                    package_name="com.ss.android.auto",
+                                                    control_id="com.ss.android.auto:id/h5q",
+                                                    content=sub_resource_name,
+                                                    timeout=5, sleep_time=5)
+
+                loggerKit.info("taskId:{0}, action25_id:{1},未根据帖子名找到对应的帖子,进行用户名搜索", device_id, action5_id)
+
+                redis_client.set(device_id + "operate", action5_id)
+                redis_client.set(f"{device_id}_step25", "1")
+                redis_client.delete(f"{device_id}_step4")
+
+                return action5_dict
+
+            # 已经点击进入帖子 判断是不是视频。如果是视频不进行操作
+
+            action4_dict = single_click_by_text(action5_id, target_app="dongchedi", target_version="7.9.0",
+                                                package_name="com.ss.android.auto",
+                                                text='视频', timeout=5, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("taskId:{0}, action5_id:{1},已进入帖子 判断是否未视频", device_id, action5_id)
+
+            return action4_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            如果有视频对应文案,则返回失败 不支持视频操作
+            如果没有获取到对应的帖子内容
+            """
+            if perform_action_result == 'success':
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+
+                del_key_vague(device_id)
+
+                callback_task(500, '该帖子为视频帖子,暂不支持', task_id, device_id, 0, None)
+
+                return return_dict
+
+            action3_dict = get_content_by_control(action6_id, target_app="dongchedi", target_version="7.9.0",
+                                                package_name="com.ss.android.auto",
+                                                control_id="", timeout=5, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action6_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)):
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            关闭app
+            """
+            action4_dict = stop_app(action4_id, target_version="7.9.0", target_app="dongchedi", timeout=5, sleep_time=3)
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_dict)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, mobile, 1, None)
+
+            return action4_dict
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 177 - 0
dongchedi/dongchedi_sign.py

@@ -0,0 +1,177 @@
+"""
+懂车帝签到
+"""
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, stop_app, start_app, \
+    single_click_by_text, click_pic
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+
+
+def dongchedi_sign(task_id, mobile, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, mobile, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, mobile, 0, None)
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击进入我的页面
+            """
+            action1_dict = single_click_by_control(action1_id, target_app="dongchedi", target_version="7.9.0",
+                                                   package_name="com.ss.android.auto",
+                                                   control_id="android:id/tabs", timeout=5, sleep_time=5,
+                                                   item_index=4)
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击进入签到元素
+            """
+            action2_dict = single_click_by_control(action2_id, target_app="dongchedi", target_version="7.9.0",
+                                                   package_name="com.ss.android.auto",
+                                                   control_id="com.ss.android.auto:id/a6l",
+                                                   timeout=5, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        dongchedi_sign_path = 'dongchedi/pic/dongchedi_sign.png'
+
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击签到
+            """
+            dongchedi_sign_base64 = pic_to_base64(dongchedi_sign_path)
+
+            action3_dict = click_pic(action3_id, target_app="dongchedi", target_version="7.9.0",
+                                     package_name="com.ss.android.auto",
+                                     pic_base64=dongchedi_sign_base64)
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)
+                                                      and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            关闭app
+            """
+            action4_dict = stop_app(action4_id, target_version="7.9.0", target_app="dongchedi", timeout=5, sleep_time=3)
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_dict)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, mobile, 1, None)
+
+            return action4_dict
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

BIN
dongchedi/pic/dongchedi_sign.png


+ 420 - 0
dongchedi/random_browse.py

@@ -0,0 +1,420 @@
+"""
+懂车帝养号
+随机浏览若页面
+"""
+import random
+import time
+
+# import numpy as np
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, swipe_screen, stop_app, start_app, back_last_page
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+
+
+def random_browse(task_id, mobile, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, mobile, 0, None)
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            首页随机滑动1~6屏
+            """
+            # random_num = np.random.choice(range(1, 6), 6)
+            # random_scale = random.choice(random_num)
+            action1_dict = swipe_screen(action1_id, scale=1, timeout=30, direction='down')
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step_start", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        step0 = redis_client.get(f"{device_id}_step_start")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            首页随机滑动1~6屏
+            """
+            # random_num = np.random.choice(range(1, 6), 6)
+            # random_scale = random.choice(random_num)
+            random_scale = random.randint(1, 6)
+            action1_dict = swipe_screen(action1_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step_start")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        """
+        搜索关键词
+        """
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击进入帖子
+            """
+            action2_dict = single_click_by_control(action2_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        # stepA = redis_client.get(f"{device_id}_step2")
+        # if stepA is not None and int(stepA) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id) and perform_action_result == "invalid operation":
+        #     """
+        #     兼容千人千面
+        #     """
+        #     actionA_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第2条指令
+        #     点击进入帖子
+        #     """
+        #     actionA_dict = single_click_by_control(actionA_id, control_id="com.ss.android.auto:id/s")
+        #
+        #     redis_client.set(device_id + "operate", actionA_id)
+        #     redis_client.set(f"{device_id}_stepA", "1")
+        #     redis_client.delete(f"{device_id}_step2")
+        #
+        #     loggerKit.info("设备:{0}, action2_id:{1}", device_id, actionA_id)
+        #
+        #     return actionA_dict
+
+        # stepA1 = redis_client.get(f"{device_id}_stepA")
+        # if stepA1 is not None and int(stepA1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id) and perform_action_result == "invalid operation":
+        #     """
+        #     兼容千人千面
+        #     """
+        #     actionA1_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第2条指令
+        #     点击进入帖子
+        #     """
+        #     actionA1_dict = single_click_by_control(actionA1_id, control_id="com.ss.android.auto:id/k2k")
+        #
+        #     redis_client.set(device_id + "operate", actionA1_id)
+        #     redis_client.set(f"{device_id}_stepA1", "1")
+        #     redis_client.delete(f"{device_id}_stepA")
+        #
+        #     loggerKit.info("设备:{0}, action2_id:{1}", device_id, actionA1_id)
+        #
+        #     return actionA1_dict
+
+        # stepA2 = redis_client.get(f"{device_id}_stepA1")
+        # if stepA2 is not None and int(stepA2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id) and perform_action_result == "invalid operation":
+        #     """
+        #     兼容千人千面
+        #     """
+        #     actionA2_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第2条指令
+        #     点击进入帖子
+        #     """
+        #     actionA2_dict = single_click_by_control(actionA2_id, control_id="com.ss.android.auto:id/p")
+        #
+        #     redis_client.set(device_id + "operate", actionA2_id)
+        #     redis_client.set(f"{device_id}_stepA2", "1")
+        #     redis_client.delete(f"{device_id}_stepA1")
+        #
+        #     loggerKit.info("设备:{0}, action2_id:{1}", device_id, actionA2_id)
+        #
+        #     return actionA2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            在帖子中随机滑动1~3屏
+            """
+            # random_num = np.random.choice(range(1, 3), 3)
+            # random_scale = random.choice(random_num)
+            random_scale = random.randint(1, 3)
+            action3_dict = swipe_screen(action3_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            # redis_client.delete(f"{device_id}_stepA")
+            # redis_client.delete(f"{device_id}_stepA1")
+            # redis_client.delete(f"{device_id}_stepA2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and (
+                last_action_id is not None and int(perform_action_id) == int(last_action_id)
+                and perform_action_result == "success"):
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            退出当前帖子
+            """
+            action1_dict = back_last_page(action4_id)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action1_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            首页随机滑动1~6屏
+            """
+            # random_num = np.random.choice(range(1, 6), 6)
+            # random_scale = random.choice(random_num)
+            random_scale = random.randint(1, 6)
+            action1_dict = swipe_screen(action5_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action1_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)
+                                                      and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            点击进入帖子
+            """
+            action2_dict = single_click_by_control(action6_id, control_id="com.ss.android.auto:id/jum",
+                                                   control_ids="com.ss.android.auto:id/s,com.ss.android.auto:id/k2k,"
+                                                               "com.ss.android.auto:id/p")
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+
+            return action2_dict
+
+        # stepB = redis_client.get(f"{device_id}_step6")
+        # if stepB is not None and int(stepB) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id) and perform_action_result == "invalid operation":
+        #     """
+        #     兼容千人千面
+        #     """
+        #     actionB_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第2条指令
+        #     点击进入帖子
+        #     """
+        #     actionB_dict = single_click_by_control(actionB_id, control_id="com.ss.android.auto:id/s")
+        #
+        #     redis_client.set(device_id + "operate", actionB_id)
+        #     redis_client.set(f"{device_id}_stepB", "1")
+        #     redis_client.delete(f"{device_id}_step6")
+        #
+        #     loggerKit.info("设备:{0}, action6_id:{1}", device_id, actionB_id)
+        #
+        #     return actionB_dict
+
+        # stepB1 = redis_client.get(f"{device_id}_stepB")
+        # if stepB1 is not None and int(stepB1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id) and perform_action_result == "invalid operation":
+        #     """
+        #     兼容千人千面
+        #     """
+        #     actionB1_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第2条指令
+        #     点击进入帖子
+        #     """
+        #     actionB1_dict = single_click_by_control(actionB1_id, control_id="com.ss.android.auto:id/k2k")
+        #
+        #     redis_client.set(device_id + "operate", actionB1_id)
+        #     redis_client.set(f"{device_id}_stepB1", "1")
+        #     redis_client.delete(f"{device_id}_stepB")
+        #
+        #     loggerKit.info("设备:{0}, action6_id:{1}", device_id, actionB1_id)
+        #
+        #     return actionB1_dict
+
+        # stepB2 = redis_client.get(f"{device_id}_stepB1")
+        # if stepB2 is not None and int(stepB2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id) and perform_action_result == "invalid operation":
+        #     """
+        #     兼容千人千面
+        #     """
+        #     actionB2_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第2条指令
+        #     点击进入帖子
+        #     """
+        #     actionB2_dict = single_click_by_control(actionB2_id, control_id="com.ss.android.auto:id/p")
+        #
+        #     redis_client.set(device_id + "operate", actionB2_id)
+        #     redis_client.set(f"{device_id}_stepB2", "1")
+        #     redis_client.delete(f"{device_id}_stepB1")
+        #
+        #     loggerKit.info("设备:{0}, action6_id:{1}", device_id, actionB2_id)
+        #
+        #     return actionB2_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if (step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            在帖子中随机滑动1~3屏
+            """
+            # random_num = np.random.choice(range(1, 3), 6)
+            # random_scale = random.choice(random_num)
+            random_scale = random.randint(1, 3)
+            action1_dict = swipe_screen(action7_id, scale=random_scale, timeout=30)
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+            # redis_client.delete(f"{device_id}_stepB")
+            # redis_client.delete(f"{device_id}_stepB1")
+            # redis_client.delete(f"{device_id}_stepB2")
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action1_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+        if (step7 is not None and int(step7) == 1 and last_action_id is not None
+                and int(perform_action_id) == int(last_action_id) and perform_action_result == "success"):
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            退出当前帖子
+            """
+            action8_dict = back_last_page(action8_id)
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_id)
+
+            return action8_dict
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and (last_action_id is not None
+                                                      and int(perform_action_id) == int(last_action_id)
+                                                      and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(last_action_id))
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            关闭app
+            """
+            action9_dict = stop_app(action9_id, target_version="7.9.0")
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_dict)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, mobile, 1, None)
+
+            return action9_dict
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 478 - 0
dongchedi/simple_comment.py

@@ -0,0 +1,478 @@
+"""
+懂车帝简单互动
+"""
+import json
+import time
+
+import httpx
+import yaml
+from urllib.parse import quote
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, send_text_by_control, single_click_by_text, \
+    get_content_by_control, stop_app, start_app
+from task.task_job import callback_task
+from tools import redis_client, loggerKit
+import requests
+import threading
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = extern_domain + config['bmp-content-center']['comment_local_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+def simple_comment(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    # title = inner_data.get("resourceName")
+    # author = inner_data.get("subResourceName")
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    title = extension_info['title']
+    author = extension_info['author']
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task1(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task1(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task1(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task1(500, '指令被用户终止', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task1(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+            del_key_vague(device_id)
+            callback_task1(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            懂车帝APP首页点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_version="7.9.0",
+                                                   control_id="com.ss.android.auto:id/c9c")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            在搜索框输入关键词
+            """
+            action2_dict = send_text_by_control(action2_id, control_id="com.ss.android.auto:id/h5q",
+                                                content=f'{author}')
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+            action3_dict = single_click_by_control(action3_id, control_id="com.ss.android.auto:id/g9e")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        # step3 = redis_client.get(f"{device_id}_step3")
+        # if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id):
+        #     loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+        #     action4_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第4条指令
+        #     点击tab->口碑
+        #     """
+        #     action4_dict = single_click_by_text(action4_id, text="二手车")
+        #     redis_client.set(device_id + "operate", action4_id)
+        #     redis_client.set(f"{device_id}_step4", "1")
+        #     redis_client.delete(f"{device_id}_step3")
+        #
+        #     loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+        #
+        #     return action4_dict
+        #
+        # step4 = redis_client.get(f"{device_id}_step4")
+        # if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id):
+        #     loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+        #     action5_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第5条指令
+        #     点击tab->车友圈
+        #     """
+        #     action5_dict = single_click_by_text(action5_id, text="车友圈")
+        #     redis_client.set(device_id + "operate", action5_id)
+        #     redis_client.set(f"{device_id}_step5", "1")
+        #     redis_client.delete(f"{device_id}_step4")
+        #
+        #     return action5_dict
+        #
+        # step5 = redis_client.get(f"{device_id}_step5")
+        # if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id):
+        #     loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+        #     action6_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第6条指令
+        #     点击tab->用户
+        #     """
+        #     action6_dict = single_click_by_text(action6_id, text="用户")
+        #
+        #     redis_client.set(device_id + "operate", action6_id)
+        #     redis_client.set(f"{device_id}_step6", "1")
+        #     redis_client.delete(f"{device_id}_step5")
+        #
+        #     return action6_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            直接点击用户名称
+            """
+            action4_dict = single_click_by_text(action4_id, text=author)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        # 判断是否有搜索结果
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            """
+            点击搜素出来的博主
+            """
+            action7_dict = single_click_by_text(action7_id, text=f"{author}")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action7_dict
+
+        # 存在搜索结果
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            存在搜索结果,已经跳转到了用户中心首页
+            开始匹配文章
+            """
+            action8_dict = get_content_by_control(action8_id, control_id="android.widget.TextView",
+                                                  title=f'{title}')
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 文章评论,点击铅笔父节点
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击评论
+            """
+            loggerKit.info("action9_id:{0}, 文章评论,点击铅笔父节点", action9_id)
+            action9_dict = single_click_by_control(action9_id, control_id="com.ss.android.auto:id/d98")
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        """
+        文章匹配,点击输入框,返回回复内容
+        """
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action10_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            loggerKit.info("action10_id:{0}, 文章title:{1}, 回复内容:{2}")
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + title, timeout=120)
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+            # 评论集合
+            comment_list = comment_body.get('data')
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, title, comment_list)
+            existing_comment = ''
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+                existing_comment = '||'.join(comment_list)
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, title, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "platform": "dcd",
+                "content": title,
+                "comment": existing_comment,
+                "mode": "1"
+            }
+
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+            loggerKit.info("任务id:{0}获取AIGC评论结果:{1}", task_id, response.text)
+
+            reply_content = "不错👍"
+            if response.is_success:
+                response_body = json.loads(response.text)
+                data = response_body.get('data')
+                reply_content = data.get('comment')
+                if len(reply_content) == 0:
+                    reply_content = "赞👍"
+
+            content_result = content_detail(title, author, reply_content, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            action10_dict = send_text_by_control(action10_id,
+                                                 control_id="com.ss.android.auto:id/byd",
+                                                 content=f'{reply_content}')
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            return action10_dict
+
+        """
+        点击发送按钮
+        """
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action11_id = int(round(time.time() * 1000))
+            """
+            文章匹配,点击输入框,返回回复内容
+            """
+            action11_dict = single_click_by_control(action11_id, control_id="com.ss.android.auto:id/jxq")
+
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            return action11_dict
+
+        """
+        停止app
+        """
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action12_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action12_dict = stop_app(action12_id, target_version="7.9.0")
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_dict)
+            reply_json = redis_client.get(device_id + "reply_json")
+            callback_task1(None, None, task_id, device_id, 1, None, reply_json)
+            del_key_vague(device_id)
+
+            return action12_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_version="7.9.0")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+def callback_task1(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

BIN
douyin/__pycache__/douyin_boxes_advise.cpython-311.pyc


BIN
douyin/__pycache__/douyin_random_search.cpython-311.pyc


BIN
douyin/__pycache__/douyin_sign.cpython-311.pyc


BIN
douyin/__pycache__/douyin_train_home_comment.cpython-311.pyc


BIN
douyin/__pycache__/douyin_train_home_page.cpython-311.pyc


BIN
douyin/__pycache__/douyin_user_operate.cpython-311.pyc


BIN
douyin/__pycache__/douyin_watch_advise.cpython-311.pyc


BIN
douyin/__pycache__/simple_browser_video.cpython-311.pyc


+ 488 - 0
douyin/atom-task/search_atricle_comment.py

@@ -0,0 +1,488 @@
+import time
+from urllib.parse import quote
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, get_text_by_control, single_click_by_control, \
+    send_text_by_control
+import threading
+import requests
+
+import yaml
+import json
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_url_dy']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+# 紧急公关 抖音互动 (根据视频链接 进行搜索获取到对应的视频进行评论)
+# version 28.8.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    summary = extension_info['summary']
+    if summary is not None and summary != '':
+        sub_resource_name = summary
+    else:
+        sub_resource_name = extension_info['title']
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/dms", timeout=5)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索按钮
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nup")
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("taskId:{0}, action4_id:{1}", task_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            如果没获取到对应的评论 任务结束 关闭
+            """
+
+            if sub_resource_name is None or sub_resource_name == '':
+                action4_dict = stop_app(action4_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                # 回调任务中心
+
+                del_key_vague(device_id)
+
+                callback_task(None, '获取评论文案的标题为null,所以未进行评论', task_id, device_id, 1, None, None)
+                loggerKit.info("taskId:{0}, action4_id_mem:{1},对应的标题为null无法获取评论文案", task_id,
+                               int(last_action_id))
+
+                return action4_dict
+
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + keyword, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, keyword, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, keyword, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "templateId": "dcd_comment",
+                "content": sub_resource_name,
+                "existingComment": existing_comment
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, keyword, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action4_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                   timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword,
+                               request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action4_id)
+                redis_client.set(f"{device_id}_step4", "1")
+                redis_client.delete(f"{device_id}_step3")
+
+                loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+                return action4_dict
+
+            response_body = json.loads(response.text)
+
+            data = response_body.get('data')
+
+            reply_text = data.get('answer')
+
+            reply_text_json = json.loads(reply_text)
+
+            reply = reply_text_json.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword,
+                               request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action4_id)
+                redis_client.set(f"{device_id}_step4", "1")
+                redis_client.delete(f"{device_id}_step3")
+
+                loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+                return action4_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("taskId:{0}, action3_id_mem:{1}", task_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击评论框
+            """
+
+            action4_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/brv", timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("taskId:{0}, action5_id:{1}", task_id, action4_id)
+
+            return action4_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("taskId:{0}, action5_id_mem:{1}", task_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                callback_task(500, '抖音回复内容为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action6_dict = send_text_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/brv",
+                                                content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            content_result = content_detail(keyword, sub_resource_name, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            发送评论
+            """
+            action7_dict = single_click_by_control(action7_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bcw", timeout=5)
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        """
+        停止app
+        """
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action11_dict = stop_app(action8_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action8_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None, reply_json)
+
+            return action11_dict
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+def callback_task(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 752 - 0
douyin/atom-task/search_atricle_like_collection.py

@@ -0,0 +1,752 @@
+"""
+官媒账号互动操作(点赞收藏评论 按账号去重)
+"""
+import time
+
+from urllib.parse import quote
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, start_app, get_content_by_control, single_click_by_text, \
+    many_click_by_position, stop_app, send_text_by_control, get_text_by_control, back_last_page, swipe_screen
+from tools import loggerKit, redis_client
+import json
+import httpx
+import threading
+import yaml
+import requests
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_library_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+def douyin_random_watch_video(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    resource_name = inner_data.get("resourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令被用户终止', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+            del_key_vague(device_id)
+            call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        # 获取到对应抖音账号并存入redis:
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取当前的抖音账号
+            获取当前抖音账号
+            """
+            action2_dict = get_text_by_control(action2_id,
+                                               control_id="com.ss.android.ugc.aweme.lite:id/ifc",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=3)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_get_account", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step1 = redis_client.get(f"{device_id}_step_get_account")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            # 获取当前登陆账号并存入缓存
+            account_name = result.get("performActionText")
+            if account_name is None:
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "未找到签到指示"
+                }
+
+                del_key_vague(device_id)
+                call_back_task_reply(500, '未获取到账号信息', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            # 将当前设备操作的账号存入redis
+            redis_client.set(device_id + 'account', account_name)
+
+            """
+            发送第2条指令
+            点击:关注
+            """
+            action2_dict = single_click_by_control(action2_id,
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/od2",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step_get_account")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击:荣威ROEWE
+            """
+            action3_dict = get_content_by_control(action3_id, title=resource_name,
+                                                  target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step5 = redis_client.get(f"{device_id}_step4")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            随机点击某个视频
+            """
+            action6_dict = single_click_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/container",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/container",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action6_dict
+
+        # 获取对应的视频时间戳
+
+        step5 = redis_client.get(f"{device_id}_step6")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取到对应视频的发布时间
+            
+            """
+            action6_dict = get_text_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/=5",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=5)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step_timestamp", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action6_dict
+
+        # 判断该视频对应账号是否已经点赞过
+        step5 = redis_client.get(f"{device_id}_step_timestamp")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            timestamp = result.get("performActionText")
+
+            redis_client.set(device_id + 'timestamp', timestamp)
+
+            if timestamp is None or timestamp == '':
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,未获取到时间戳,无法判断是否执行过', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            """
+            根据发布时间和用户账号 判断该用户是否对该帖子进行过操作
+            未操作过开始进行点赞 已经操作过则下滑下一个
+
+            """
+            # redis中获取到对应账号
+            account = redis_client.get(device_id + 'account')
+
+            # 获取账号已做过的帖子集合
+            done_list = redis_client.lrange('douyin_done:' + account, 0, -1)
+
+            if done_list is None:
+                # 如果处理视频集合为空 表示未处理过视频 进行点赞收藏
+                action7_dict = many_click_by_position(action6_id, target_app="douyin",
+                                                      target_version="28.8.0",
+                                                      package_name="com.ss.android.ugc.aweme.lite")
+
+                redis_client.set(device_id + "operate", action6_id)
+                redis_client.set(f"{device_id}_step7", "1")
+                redis_client.delete(f"{device_id}_step_timestamp")
+                loggerKit.info("task:{0}账号{1}未对视频{2}进行过处理,开始进行点赞", task_id, account, timestamp)
+                return action7_dict
+
+            if timestamp in done_list:
+                # 添加滑动次数
+                swipe_count = redis_client.incr(device_id + "swipe_count", 1)
+                # 从0开始计数 滑动五次结束
+                if swipe_count >= 5:
+                    action7_dict = stop_app(action6_id, target_app="douyin", target_version="28.8.0",
+                                            package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                    loggerKit.info("task:{0}账号{1}已对官媒{2}前五条视频做过处理", task_id, account, resource_name)
+
+                    # 回调任务中心修改任务状态
+                    call_back_task_reply(None, "账号:" + account + "已经对账号:" + resource_name + "前五条视频进行过处理", task_id,
+                                         device_id, 1, None, None)
+
+                    del_key_vague(device_id)
+
+                    return action7_dict
+
+                # 如果该视频已经在已操作集合中 进行下滑
+                action7_dict = swipe_screen(action6_id, scale=1,
+                                            target_app="douyin",
+                                            target_version="28.8.0",
+                                            package_name="com.ss.android.ugc.aweme.lite")
+
+                redis_client.set(device_id + "operate", action6_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step_timestamp")
+                loggerKit.info("task:{0}账号{1}已对视频{2}进行过处理,进行下滑操作", task_id, account, timestamp)
+
+                return action7_dict
+
+            action7_dict = single_click_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/cur",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step_timestamp")
+            return action7_dict
+
+        # 视频收藏
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            视频收藏->长按收藏
+            """
+            action8_dict = single_click_by_control(action8_id, control_id="com.ss.android.ugc.aweme.lite:id/bn=",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 获取帖子标题
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            获取帖子标题
+            """
+            action9_dict = get_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/desc", timeout=5)
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            """
+            result_text = result.get("performActionText")
+
+            # if result_text is None:
+            #     return_dict = {
+            #         "data": "",
+            #         "code": -2,
+            #         "message": "fail, result_text is null"
+            #     }
+            #
+            #     # 回调任务中心
+            #
+            #     del_key_vague(device_id)
+            #
+            #     callback_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+            #
+            #     return return_dict
+
+            redis_client.set(device_id + "douyin" + "comments_title", result_text)
+
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + result_text, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, result_text, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, result_text, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "mode": "1",
+                "content": result_text,
+                "existingComment": existing_comment
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, result_text, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                    timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            response_body = json.loads(response.text)
+
+            reply = response_body.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(last_action_id))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            点击评论框
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action11_dict = single_click_by_control(action11_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/brv", timeout=5)
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action12_id_mem:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action12_dict = send_text_by_control(action12_id, target_app="douyin", target_version="28.8.0",
+                                                 package_name="com.ss.android.ugc.aweme.lite",
+                                                 control_id="com.ss.android.ugc.aweme.lite:id/brv",
+                                                 content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            comment_title = redis_client.get(device_id + "douyin" + "comments_title")
+
+            content_result = content_detail(resource_name, comment_title, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            发送评论
+            """
+            action12_dict = single_click_by_control(action13_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/bcz", timeout=5)
+            redis_client.set(device_id + "operate", action13_id)
+            redis_client.set(f"{device_id}_step13", "1")
+            redis_client.delete(f"{device_id}_step12")
+
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+        step13 = redis_client.get(f"{device_id}_step13")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action14_id = int(round(time.time() * 1000))
+
+            # 评论发送成功,将帖子表识存入到已处理过的集合中
+            account = redis_client.get(device_id + 'account')
+            title = redis_client.get(device_id + 'timestamp')
+
+            redis_client.left_push('douyin_done:' + account, title)
+            """
+            发送第14条指令
+            返回上一页
+            """
+            action14_dict = back_last_page(action14_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite", sleep_time=3)
+            redis_client.set(device_id + "operate", action14_id)
+            redis_client.set(f"{device_id}_step14", "1")
+            redis_client.delete(f"{device_id}_step13")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action14_id)
+
+            return action14_dict
+
+        step14 = redis_client.get(f"{device_id}_step14")
+        if step14 is not None and int(step14) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action15_id = int(round(time.time() * 1000))
+            """
+            发送第15条指令
+            浏览90s
+            """
+            action15_dict = many_click_by_position(action15_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite", count=1, sleep_time=90)
+            redis_client.set(device_id + "operate", action15_id)
+            redis_client.set(f"{device_id}_step15", "1")
+            redis_client.delete(f"{device_id}_step14")
+
+            loggerKit.info("设备:{0}, action15_id:{1}", device_id, action15_id)
+
+            return action15_dict
+
+        """
+        停止app
+        """
+        step15 = redis_client.get(f"{device_id}_step15")
+        if step15 is not None and int(step15) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action14_id_mem:{1}", device_id, int(last_action_id))
+
+            action16_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action14_dict = stop_app(action16_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action16_id)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            # 回调任务中心修改任务状态
+            call_back_task_reply(None, None, task_id, device_id, 1, None, reply_json)
+
+            del_key_vague(device_id)
+
+            return action14_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+def call_back_task_reply(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 8 - 0
douyin/atom-task/test_poc.py

@@ -0,0 +1,8 @@
+"""
+test_poc
+原子化脚本
+"""
+
+
+def test_poc(task_id, device_id, data):
+    pass

+ 335 - 0
douyin/douyin_boxes_advise.py

@@ -0,0 +1,335 @@
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen, single_click_by_control, \
+    check_pic_exist, \
+    click_pic, get_content_by_control, swipe_screen
+
+import requests
+import json
+import threading
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音极速版开宝箱+看广告
+# version 28.8.0
+def douyin_spider(device_serial, mobile, task_id, keyword, media_channel, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "picNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行领取宝箱看广告任务', task_id, mobile, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, mobile, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击急速版抖音赚钱按键
+
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/hvy",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/hvy,"
+                                                               "com.ss.android.ugc.aweme.lite:id/lf3")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+
+        #  点击领取宝箱
+        boxes_path = 'boxes.png'
+
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击宝箱领取宝箱奖品
+            """
+            boxes_base64 = pic_to_base64(boxes_path)
+
+            action2_dict = click_pic(action2_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite",
+                                     pic_base64=boxes_base64)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        # 点击宝箱后会弹出广告跳转
+        boxes_advise_path = 'box_advise.png'
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击宝箱后 领取成功会广告跳转 没有则表示未领取成功 可能已经领取过
+            """
+            boxes_advice_base64 = pic_to_base64(boxes_advise_path)
+
+            action3_dict = check_pic_exist(action3_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite",
+                                           pic_base64=boxes_advice_base64)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击看广告进行观看
+            """
+            boxes_advice_base64 = pic_to_base64(boxes_advise_path)
+
+            action3_dict = click_pic(action4_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite",
+                                     pic_base64=boxes_advice_base64)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            判断是否有返回元素    某些账号观看广告是下滑瀑布流(小o2024)需要进行下滑处理
+            """
+
+            action3_dict = get_content_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                  max_page="1",
+                                                  package_name="com.ss.android.ugc.aweme.lite", operator_type=1,
+                                                  title="返回")
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        """
+        根据返回 进行持续下滑或不操作
+        """
+        step5 = redis_client.get(f"{device_id}_step5")
+
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            判断是否有返回元素    某些账号观看广告是下滑瀑布流(小o2024)需要进行下滑处理
+            """
+
+            if perform_action_result == 'eleNotFound':
+                action3_dict = swipe_screen(action5_id,
+                                            package_name="com.ss.android.ugc.aweme.lite", sleep_time=70)
+            else:
+                action3_dict = continual_swipe_screen(action5_id,
+                                                      package_name="com.ss.android.ugc.aweme.lite",
+                                                      continuous_time=90)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action3_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action5_dict = stop_app(action5_id, target_app="douyin", target_version="28.8.0",
+                                    package_name="com.ss.android.ugc.aweme.lite")
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action5_dict)
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, mobile, 1, None)
+
+            return action5_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+# post请求
+# def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+#     response = {
+#         "errorCode": error_code,
+#         "errorMsg": error_msg,
+#         "taskId": task_id,
+#         "taskStatus": task_status,
+#         "taskExecuteResponse": task_execute_response,
+#         "content": content,
+#         "demoFlag": demo_flag
+#     }
+#     return response
+#
+#
+# # 回调任务中心接口
+# def callback_task(err_code, err_msg, task_id, device_id, execute_status, result):
+#     callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, None, None)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行结果:{5}, result:{6}  。回调开始", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, task_callback_url, execute_status, result)
+#     current_timeout = (5, 10)
+#     callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False))
+#     return callback_response2

+ 653 - 0
douyin/douyin_comments_operate.py

@@ -0,0 +1,653 @@
+# 抖音评论正负向判断、点赞
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, get_text_by_control, single_click_by_control, \
+    send_text_by_control, single_click_by_text, swipe_screen, spider_content_parent_find_click, \
+    get_text_by_control_comment
+
+import yaml
+import json
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 判断好评url
+comment_direction_url = extern_domain + config['bmp-content-center']['comment_direction_url']
+
+# 评论成功记录url
+user_operate_add_url = extern_domain + config['bmp-cp']['user_operate_add_url']
+
+# 判断是否有该评论url
+user_operate_is_like_url = extern_domain + config['bmp-cp']['user_operate_is_like_url']
+
+
+# 抖音互动 (抖音评论正负向判断、点赞)
+# version 29.5.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    extend_info = data['data']['extendInfo']
+    extend_info = json.loads(extend_info)
+    keyword = extend_info['url']
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step_me", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        # 获取到对应抖音账号并存入redis:
+        step1 = redis_client.get(f"{device_id}_step_me")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取当前的抖音账号
+            获取当前抖音账号
+            """
+            action2_dict = get_text_by_control(action2_id,
+                                               control_id="com.ss.android.ugc.aweme.lite:id/ib7",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=3)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_get_account", "1")
+            redis_client.delete(f"{device_id}_step_me")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step1 = redis_client.get(f"{device_id}_step_get_account")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            # 获取当前登陆账号并存入缓存
+            account_name = result.get("performActionText")
+            if account_name is None:
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "未找到签到指示"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '未获取到账号信息', task_id, device_id, 0, None)
+
+                return return_dict
+
+            # 将当前设备操作的账号存入redis
+            redis_client.set(device_id + 'account', account_name)
+
+            """
+            发送第2条指令
+            点击:首页
+            """
+            action2_dict = single_click_by_text(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="首页")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_search", "1")
+            redis_client.delete(f"{device_id}_step_get_account")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step0 = redis_client.get(f"{device_id}_step_search")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '回到首页失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/dxw", timeout=5)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step_search")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '点击搜索按钮失败', task_id, device_id, 0, None)
+                return return_dict
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '搜索框赋值失败', task_id, device_id, 0, None)
+                return return_dict
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nou", timeout=5)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '搜索失败', task_id, device_id, 0, None)
+                return return_dict
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            打开评论区
+            """
+
+            action3_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bfw", timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '打开评论框失败', task_id, device_id, 0, None)
+                return return_dict
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            放大评论区
+            """
+
+            action5_dict = single_click_by_control(action5_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/d+4", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '放大评论区/点赞评论失败', task_id, device_id, 0, None)
+                return return_dict
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            1.判断是否已经执行过20次,如果超过20次 结束任务
+            如果没有超过20次获取评论信息 任务继续
+            2.评论下标记录
+
+            """
+
+            finished_count = redis_client.get(device_id + 'finish_comment')
+
+            if finished_count is not None and int(finished_count) >= 20:
+                """
+                停止指令
+                停止app
+                """
+
+                action7_dict = stop_app(action7_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+
+                loggerKit.info("设备:{0}, action11_id:{1}", device_id, action7_id)
+
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, None)
+
+                return action7_dict
+
+            # 对应评论下标
+            comment_index = redis_client.incr(device_id + 'comment_index', 1)
+
+            # 获取评论内容
+            action7_dict = get_text_by_control_comment(action7_id, target_app="douyin", target_version="28.8.0",
+                                                       package_name="com.ss.android.ugc.aweme.lite",
+                                                       control_id="com.ss.android.ugc.aweme.lite:id/content", timeout=5,
+                                                       item_index=comment_index - 1, sleep_time=5)
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                action7_dict = stop_app(action8_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+                callback_task(500, '获取评论信息失败', task_id, device_id, 0, None)
+                return action7_dict
+
+            # 下标越界
+            if perform_action_result == "indexOutOfBounds":
+                # 判断是否有 '暂时没有更多了' 文案
+
+                action8_dict = single_click_by_text(action8_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    text="暂时没有更多了", sleep_time=5)
+
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.delete(f"{device_id}_step7")
+                redis_client.set(f"{device_id}_swipe_comment", "1")
+
+                return action8_dict
+
+            # 获取到对应的评论文案
+            result_text = result.get("performActionText")
+
+            # 如果获取到的评论文案是空的 再次获取
+
+            if result_text is None or result_text == '':
+                action_dict = get_text_by_control_comment(action8_id, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step7")
+                return action_dict
+
+            """
+            发送第8条指令
+            判断该评论是否是正向评论
+            """
+            # 调用后台接口,判断该账号是否已经对对应帖子评论点赞过
+            account_name = redis_client.get(device_id + 'account')
+            judge_params = {"fromUser": account_name, "resource": keyword, "subResource": result_text}
+            judge_response = httpx.get(user_operate_is_like_url, params=judge_params)
+            loggerKit.info('taskId:{0}judge_comment_api:{1},返回结果:{2}', task_id, judge_params, judge_response.text)
+
+            # 是否未操作过
+            if judge_response.is_success:
+                judge_body = json.loads(judge_response.text)
+                judge_data = judge_body.get('data')
+
+                if int(judge_data) == 1:
+                    loggerKit.info('taskId:{0}judge_comment_api已经点赞过:{1}', task_id, judge_params)
+                    operated = True
+                else:
+                    operated = False
+            else:
+                loggerKit.info('taskId:{0}judge_comment_api是否点赞接口调用失败:{1}', task_id, judge_params)
+                operated = True
+
+            if operated:
+                loggerKit.info("taskId:{0},对应评论为:{1}已经可能点赞过了", task_id, judge_params)
+                action_dict = get_text_by_control_comment(action8_id, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0, sleep_time=5)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step7")
+                redis_client.incr(device_id + 'finish_comment', 1)
+
+                return action_dict
+
+            # 如果未点赞过 判断该评论正负向
+            # 是否为正向评论
+
+            direction_params = {
+                "comment": result_text.replace('首评', '')
+            }
+            direction_response = httpx.post(comment_direction_url, json=direction_params, timeout=120)
+            loggerKit.info('taskId:{0}判断正负向接口调用:{1},返回结果:{2}', task_id, direction_params, direction_response.text)
+
+            if direction_response.is_success:
+
+                direction_data = json.loads(direction_response.text)
+                data = direction_data.get('data')
+                direction = data.get('direction')
+                if direction == 'positive':
+                    need_operate = True
+                else:
+                    need_operate = False
+
+            else:
+                loggerKit.info('taskId:{0}判断正负向接口调用失败:{1}', task_id, direction_params)
+                need_operate = False
+
+            if not need_operate:
+                loggerKit.info("taskId:{0},对应评论为:{1}不需要点赞", task_id, result_text)
+                action_dict = get_text_by_control_comment(action8_id, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0, sleep_time=5)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step7")
+                redis_client.incr(device_id + 'finish_comment', 1)
+
+                return action_dict
+
+            action5_dict = spider_content_parent_find_click(action8_id, target_app="douyin", target_version="28.8.0",
+                                                            package_name="com.ss.android.ugc.aweme.lite",
+                                                            title=result_text,
+                                                            child_control_id="com.ss.android.ugc.aweme.lite:id/cth",
+                                                            item_index=3,
+                                                            timeout=5, sleep_time=5)
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+            # 将当前评论存入缓存中
+            redis_client.set(f"{device_id}_comment", result_text)
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action8_id)
+
+            return action5_dict
+
+        step_swipe_comment = redis_client.get(f"{device_id}_swipe_comment")
+        if step_swipe_comment is not None and int(step_swipe_comment) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action_swipe_id = int(round(time.time() * 1000))
+
+            # 元素未找到 代表没有 '暂时没有更多了' 继续下滑, 且对下标记录重置从0开始
+            if perform_action_result == "invalid operation":
+                action5_dict = swipe_screen(action_swipe_id, target_app="douyin", target_version="28.8.0",
+                                            package_name="com.ss.android.ugc.aweme.lite",
+                                            timeout=5, duration=2000, sleep_time=5)
+                redis_client.set(device_id + "operate", action_swipe_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_swipe_comment")
+                loggerKit.info("设备:{0}, action2_id:{1}", device_id, action_swipe_id)
+                redis_client.delete(device_id + 'comment_index')
+
+                return action5_dict
+
+            # 没有更多评论 回调任务中心
+            stop_app(action_swipe_id, target_app="douyin", target_version="28.8.0",
+                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            del_key_vague(device_id)
+            callback_task(None, None, task_id, device_id, 1, None)
+            return stop_app
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            step9 = int(round(time.time() * 1000))
+
+            # 元素未找到 代表没有点赞成功,继续获取下一个评论
+            if perform_action_result == "invalid operation":
+                action_dict = get_text_by_control_comment(step9, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0, sleep_time=5)
+                redis_client.set(device_id + "operate", step9)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step8")
+
+                return action_dict
+
+            # 点赞成功,评论点赞完成次数+1 且调用已完成评论接口
+            action_dict = get_text_by_control_comment(step9, target_app="douyin", target_version="28.8.0",
+                                                      package_name="com.ss.android.ugc.aweme.lite",
+                                                      control_id="com.ss.android.ugc.aweme.lite:id/content", timeout=5,
+                                                      item_index=0, sleep_time=5)
+            redis_client.set(device_id + "operate", step9)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            account_name = redis_client.get(device_id + 'account')
+
+            comment = redis_client.get(f"{device_id}_comment")
+
+            operate_add_params = {
+                "fromUser": account_name,
+                "resource": keyword,
+                "subResource": comment
+            }
+            operate_add_response = httpx.post(user_operate_add_url, json=operate_add_params, timeout=120)
+            loggerKit.info('taskId:{0}评论成功接口调用:{1},返回结果:{2}', task_id, operate_add_params, operate_add_response.text)
+
+            redis_client.incr(device_id + 'finish_comment', 1)
+            return action_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")

+ 660 - 0
douyin/douyin_designate_user_comments_operate.py

@@ -0,0 +1,660 @@
+# 抖音评论正负向判断、点赞
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, get_text_by_control, single_click_by_control, \
+    send_text_by_control, single_click_by_text, swipe_screen, spider_content_parent_find_click, \
+    get_text_by_control_comment
+
+import yaml
+import json
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 判断好评url
+comment_direction_url = extern_domain + config['bmp-content-center']['comment_direction_url']
+
+# 评论成功记录url
+user_operate_add_url = extern_domain + config['bmp-cp']['user_operate_add_url']
+
+# 判断是否有该评论url
+user_operate_is_like_url = extern_domain + config['bmp-cp']['user_operate_is_like_url']
+
+
+# 抖音互动 (抖音指定用户评论点赞)
+# version 29.5.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    extend_info = data['data']['extendInfo']
+    extend_info = json.loads(extend_info)
+    keyword = extend_info['url']
+    comment = extend_info['comment']
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step_me", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        # 获取到对应抖音账号并存入redis:
+        step1 = redis_client.get(f"{device_id}_step_me")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取当前的抖音账号
+            获取当前抖音账号
+            """
+            action2_dict = get_text_by_control(action2_id,
+                                               control_id="com.ss.android.ugc.aweme.lite:id/ib7",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=3)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_get_account", "1")
+            redis_client.delete(f"{device_id}_step_me")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step1 = redis_client.get(f"{device_id}_step_get_account")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            # 获取当前登陆账号并存入缓存
+            account_name = result.get("performActionText")
+            if account_name is None:
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "未找到签到指示"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '未获取到账号信息', task_id, device_id, 0, None)
+
+                return return_dict
+
+            # 将当前设备操作的账号存入redis
+            redis_client.set(device_id + 'account', account_name)
+
+            judge_params = {"fromUser": account_name, "resource": keyword, "subResource": comment}
+            judge_response = httpx.get(user_operate_is_like_url, params=judge_params)
+            loggerKit.info('taskId:{0}judge_comment_api:{1},返回结果:{2}', task_id, judge_params, judge_response.text)
+
+            # 是否未操作过
+            if judge_response.is_success:
+                judge_body = json.loads(judge_response.text)
+                judge_data = judge_body.get('data')
+
+                if int(judge_data) == 1:
+                    loggerKit.info('taskId:{0}judge_comment_api已经点赞过:{1}', task_id, judge_params)
+                    operated = True
+                else:
+                    operated = False
+            else:
+                loggerKit.info('taskId:{0}judge_comment_api是否点赞接口调用失败:{1}', task_id, judge_params)
+                operated = True
+
+            if operated:
+                loggerKit.info('taskId:{0}judge_comment_api已经点赞过:{1}', task_id, judge_params)
+
+                action7_dict = stop_app(action2_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+
+                loggerKit.info("设备:{0}, action11_id:{1}", device_id, action2_id)
+
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, f'用户:{account_name}已经点赞过{comment}')
+
+                return action7_dict
+
+            """
+            发送第2条指令
+            点击:首页
+            """
+            action2_dict = single_click_by_text(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="首页")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_search", "1")
+            redis_client.delete(f"{device_id}_step_get_account")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step0 = redis_client.get(f"{device_id}_step_search")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '回到首页失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/dxw", timeout=5)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step_search")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '点击搜索按钮失败', task_id, device_id, 0, None)
+                return return_dict
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '搜索框赋值失败', task_id, device_id, 0, None)
+                return return_dict
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nou", timeout=5)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '搜索失败', task_id, device_id, 0, None)
+                return return_dict
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            打开评论区
+            """
+
+            action3_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bfw", timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '打开评论框失败', task_id, device_id, 0, None)
+                return return_dict
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            放大评论区
+            """
+
+            action5_dict = single_click_by_control(action5_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/d+4", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '放大评论区/点赞评论失败', task_id, device_id, 0, None)
+                return return_dict
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            1.判断向下滑动次数是否大于50次
+            如果没有大于50次
+            2.评论下标记录
+
+            """
+
+            swipe_count = redis_client.get(device_id + 'swipe_count')
+
+            if swipe_count is not None and int(swipe_count) >= 50:
+                """
+                停止指令
+                停止app
+                """
+
+                action7_dict = stop_app(action7_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+
+                loggerKit.info("设备:{0}, action11_id:{1}", device_id, action7_id)
+
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, None)
+
+                return action7_dict
+
+            # 对应评论下标
+            comment_index = redis_client.incr(device_id + 'comment_index', 1)
+
+            # 获取评论内容
+            action7_dict = get_text_by_control_comment(action7_id, target_app="douyin", target_version="28.8.0",
+                                                       package_name="com.ss.android.ugc.aweme.lite",
+                                                       control_id="com.ss.android.ugc.aweme.lite:id/content", timeout=5,
+                                                       item_index=comment_index - 1, sleep_time=5)
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                action7_dict = stop_app(action8_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+                callback_task(500, '获取评论信息失败', task_id, device_id, 0, None)
+                return action7_dict
+
+            # 下标越界
+            if perform_action_result == "indexOutOfBounds":
+                # 判断是否有 '暂时没有更多了' 文案
+
+                action8_dict = single_click_by_text(action8_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    text="暂时没有更多了", sleep_time=5)
+
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.delete(f"{device_id}_step7")
+                redis_client.set(f"{device_id}_swipe_comment", "1")
+
+                return action8_dict
+
+            # 获取到对应的评论文案
+            result_text = result.get("performActionText")
+
+            # 如果获取到的评论文案是空的 再次获取
+
+            if result_text is None or result_text == '':
+                action_dict = get_text_by_control_comment(action8_id, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step7")
+                return action_dict
+
+            """
+            发送第8条指令
+            判断该评论是否为需点赞评论
+            """
+            if not result_text == comment:
+                loggerKit.info("taskId:{0},抓取到的评论{1}与需点赞评论不匹配{2}", task_id, result_text, comment)
+                action_dict = get_text_by_control_comment(action8_id, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0, sleep_time=5)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step7")
+
+                return action_dict
+
+            # 调用后台接口,判断该账号是否已经对对应帖子评论点赞过
+            account_name = redis_client.get(device_id + 'account')
+            judge_params = {"fromUser": account_name, "resource": keyword, "subResource": comment}
+            judge_response = httpx.get(user_operate_is_like_url, params=judge_params)
+            loggerKit.info('taskId:{0}judge_comment_api:{1},返回结果:{2}', task_id, judge_params, judge_response.text)
+
+            # 是否未操作过
+            if judge_response.is_success:
+                judge_body = json.loads(judge_response.text)
+                judge_data = judge_body.get('data')
+
+                if int(judge_data) == 1:
+                    loggerKit.info('taskId:{0}judge_comment_api已经点赞过:{1}', task_id, judge_params)
+                    operated = True
+                else:
+                    operated = False
+            else:
+                loggerKit.info('taskId:{0}judge_comment_api是否点赞接口调用失败:{1}', task_id, judge_params)
+                operated = True
+
+            if operated:
+                loggerKit.info('taskId:{0}judge_comment_api已经点赞过:{1}', task_id, judge_params)
+
+                action8_dict = stop_app(action8_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+
+                loggerKit.info("设备:{0}, action11_id:{1}", device_id, action8_id)
+
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, f'用户:{account_name}已经点赞过{comment}')
+
+                return action8_dict
+
+            action5_dict = spider_content_parent_find_click(action8_id, target_app="douyin", target_version="28.8.0",
+                                                            package_name="com.ss.android.ugc.aweme.lite",
+                                                            title=result_text,
+                                                            child_control_id="com.ss.android.ugc.aweme.lite:id/cth",
+                                                            item_index=3,
+                                                            timeout=5, sleep_time=5)
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action8_id)
+
+            return action5_dict
+
+        step_swipe_comment = redis_client.get(f"{device_id}_swipe_comment")
+        if step_swipe_comment is not None and int(step_swipe_comment) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action_swipe_id = int(round(time.time() * 1000))
+
+            # 元素未找到 代表没有 '暂时没有更多了' 继续下滑, 且对下标记录重置从0开始
+            if perform_action_result == "invalid operation":
+                action5_dict = swipe_screen(action_swipe_id, target_app="douyin", target_version="28.8.0",
+                                            package_name="com.ss.android.ugc.aweme.lite",
+                                            timeout=5, duration=2000, sleep_time=5)
+                redis_client.set(device_id + "operate", action_swipe_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_swipe_comment")
+                loggerKit.info("设备:{0}, action2_id:{1}", device_id, action_swipe_id)
+                redis_client.delete(device_id + 'comment_index')
+                redis_client.incr(device_id + 'swipe_count',1)
+
+                return action5_dict
+
+            # 没有更多评论 回调任务中心
+            stop_app(action_swipe_id, target_app="douyin", target_version="28.8.0",
+                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            del_key_vague(device_id)
+            callback_task(None, None, task_id, device_id, 1, None)
+            return stop_app
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(
+                perform_action_id) == int(last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            step9 = int(round(time.time() * 1000))
+
+            # 元素未找到 代表没有点赞成功,继续获取下一个评论
+            if perform_action_result == "invalid operation":
+                action_dict = get_text_by_control_comment(step9, target_app="douyin", target_version="28.8.0",
+                                                          package_name="com.ss.android.ugc.aweme.lite",
+                                                          control_id="com.ss.android.ugc.aweme.lite:id/content",
+                                                          timeout=5,
+                                                          item_index=0, sleep_time=5)
+                redis_client.set(device_id + "operate", step9)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step8")
+
+                return action_dict
+
+            # 点赞成功,评论点赞完成次数+1 且调用已完成评论接口
+            action_dict = get_text_by_control_comment(step9, target_app="douyin", target_version="28.8.0",
+                                                      package_name="com.ss.android.ugc.aweme.lite",
+                                                      control_id="com.ss.android.ugc.aweme.lite:id/content", timeout=5,
+                                                      item_index=0, sleep_time=5)
+            redis_client.set(device_id + "operate", step9)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            account_name = redis_client.get(device_id + 'account')
+
+            operate_add_params = {
+                "fromUser": account_name,
+                "resource": keyword,
+                "subResource": comment
+            }
+            operate_add_response = httpx.post(user_operate_add_url, json=operate_add_params, timeout=120)
+            loggerKit.info('taskId:{0}评论成功接口调用:{1},返回结果:{2}', task_id, operate_add_params, operate_add_response.text)
+
+            return action_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")

+ 444 - 0
douyin/douyin_interaction_search.py

@@ -0,0 +1,444 @@
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, get_text_by_control, single_click_by_control, \
+    send_text_by_control
+
+import yaml
+import json
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_url']
+
+
+# 抖音互动 (根据视频链接 进行搜索获取到对应的视频进行点赞 收藏 评论)
+# version 28.8.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/dms", timeout=5)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索按钮
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nup")
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            进行点赞
+            """
+            action4_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/cug", timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            收藏
+            """
+            action5_dict = single_click_by_control(action5_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bn=", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            获取到用户的帖子内容
+            """
+            action6_dict = get_text_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/desc", timeout=5)
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            如果没获取到对应的评论 任务结束 关闭
+            """
+            result_text = result.get("performActionText")
+
+            if result_text is None or result_text == '':
+                action7_dict = stop_app(action7_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                # 回调任务中心
+
+                del_key_vague(device_id)
+
+                callback_task(None, '获取到的内容标题为null,所以未进行评论', task_id, device_id, 1, None)
+
+                return action7_dict
+
+            request_data = {
+                "templateId": "dcd_comment",
+                "content": result_text,
+                "existingComment": None
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, keyword, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action7_dict = single_click_by_control(action7_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                   timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action7_id)
+                redis_client.set(f"{device_id}_step7", "1")
+                redis_client.delete(f"{device_id}_step6")
+
+                loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+                return action7_dict
+
+            response_body = json.loads(response.text)
+
+            data = response_body.get('data')
+
+            reply_text = data.get('answer')
+
+            reply_text_json = json.loads(reply_text)
+
+            reply = reply_text_json.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action7_id)
+                redis_client.set(f"{device_id}_step7", "1")
+                redis_client.delete(f"{device_id}_step6")
+
+                loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+                return action7_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            点击评论框
+            """
+
+            action8_dict = single_click_by_control(action8_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/brv", timeout=5)
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_id)
+
+            return action8_dict
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                callback_task(500, '抖音回复内容为null,无法评论', task_id, device_id, 0, None)
+
+                return return_dict
+
+            action9_dict = send_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/brv",
+                                                content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_id)
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            发送评论
+            """
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/bcw", timeout=5)
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        """
+        停止app
+        """
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action11_dict = stop_app(action11_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action11_dict
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")

+ 489 - 0
douyin/douyin_interaction_urgent.py

@@ -0,0 +1,489 @@
+import time
+from urllib.parse import quote
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, get_text_by_control, single_click_by_control, \
+    send_text_by_control
+import threading
+import requests
+
+import yaml
+import json
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = extern_domain + config['bmp-content-center']['comment_local_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+# 紧急公关 抖音互动 (根据视频链接 进行搜索获取到对应的视频进行评论)
+# version 29.5.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    keyword = extension_info['url']
+    summary = extension_info['summary']
+    if summary is not None and summary != '':
+        sub_resource_name = summary
+    else:
+        sub_resource_name = extension_info['title']
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/fsf", timeout=5)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索按钮
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nou")
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("taskId:{0}, action4_id:{1}", task_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            如果没获取到对应的评论 任务结束 关闭
+            """
+
+            if sub_resource_name is None or sub_resource_name == '':
+                action4_dict = stop_app(action4_id, target_app="douyin", target_version="28.8.0",
+                                        package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                # 回调任务中心
+
+                del_key_vague(device_id)
+
+                callback_task(None, '获取评论文案的标题为null,所以未进行评论', task_id, device_id, 1, None, None)
+                loggerKit.info("taskId:{0}, action4_id_mem:{1},对应的标题为null无法获取评论文案", task_id,
+                               int(last_action_id))
+
+                return action4_dict
+
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + keyword, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, keyword, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, keyword, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "platform": "douyin",
+                "content": sub_resource_name,
+                "comment": existing_comment,
+                "mode": "1"
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, keyword, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            loggerKit.info("任务id:{0}对应标题:{1},AIGC返回信息:{2},接口:{3}", task_id, keyword, response,post_ai_gc_url)
+
+
+            # 点击评论框
+            action4_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                   timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword,
+                               request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action4_id)
+                redis_client.set(f"{device_id}_step4", "1")
+                redis_client.delete(f"{device_id}_step3")
+
+                loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+                return action4_dict
+
+            response_body = json.loads(response.text)
+
+            reply_data = response_body.get('data')
+
+            reply = reply_data.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword,
+                               request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action4_id)
+                redis_client.set(f"{device_id}_step4", "1")
+                redis_client.delete(f"{device_id}_step3")
+
+                loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+                return action4_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("taskId:{0}, action3_id_mem:{1}", task_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击评论框
+            """
+
+            action4_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bq0", timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("taskId:{0}, action5_id:{1}", task_id, action4_id)
+
+            return action4_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("taskId:{0}, action5_id_mem:{1}", task_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                callback_task(500, '抖音回复内容为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action6_dict = send_text_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/bq0",
+                                                content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            content_result = content_detail(keyword, sub_resource_name, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            发送评论
+            """
+            action7_dict = single_click_by_control(action7_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bg=", timeout=5)
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        """
+        停止app
+        """
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action11_dict = stop_app(action8_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action8_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None, reply_json)
+
+            return action11_dict
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+def callback_task(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 675 - 0
douyin/douyin_live_comment.py

@@ -0,0 +1,675 @@
+# 抖音直播间评论脚本
+import random
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, get_text_by_control, single_click_by_control, \
+    send_text_by_control, many_click_by_position, single_click_by_text
+
+import yaml
+import json
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = extern_domain + config['bmp-content-center']['comment_local_url']
+
+
+# 抖音互动 (根据视频链接 进行搜索获取到对应的视频进行点赞 收藏 评论)
+# version 29.5.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    keyword = extension_info['liveURL']
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step_me", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        # 获取到对应抖音账号并存入redis:
+        step1 = redis_client.get(f"{device_id}_step_me")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取当前的抖音账号
+            获取当前抖音账号
+            """
+            action2_dict = get_text_by_control(action2_id,
+                                               control_id="com.ss.android.ugc.aweme.lite:id/ib7",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=3)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_get_account", "1")
+            redis_client.delete(f"{device_id}_step_me")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step1 = redis_client.get(f"{device_id}_step_get_account")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            # 获取当前登陆账号并存入缓存
+            account_name = result.get("performActionText")
+            if account_name is None:
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "未获取到账号信息"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '未获取到账号信息', task_id, device_id, 0, None)
+
+                return return_dict
+
+            # 将当前设备操作的账号存入redis
+            redis_client.set(device_id + 'account', account_name)
+
+            """
+            发送第2条指令
+            点击:首页
+            """
+            action2_dict = single_click_by_text(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="首页")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_search", "1")
+            redis_client.delete(f"{device_id}_step_get_account")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step0 = redis_client.get(f"{device_id}_step_search")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '回到首页失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击搜索框
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/dxw", timeout=5)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step_search")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '点击搜索按钮失败', task_id, device_id, 0, None)
+                return return_dict
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '搜索框赋值失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索按钮
+            """
+
+            comment_count = random.randint(5, 8)
+            redis_client.set(device_id + 'comment_count', comment_count)
+            loggerKit.info("任务id:{0}, 评论次数:{1}", task_id, comment_count)
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nou")
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        # 第四步 判断是否可分享 不可分享开始评论
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '搜索框赋值失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action4_id = int(round(time.time() * 1000))
+
+            account_name = redis_client.get(device_id + 'account')
+
+            # 判断该设备是否对该链接进行过转发
+            done_link = redis_client.set_nx(account_name + keyword, 'done')
+            if done_link:
+                """
+                发送第4条指令
+                点击更多 开始分享
+                """
+                action6_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                       package_name="com.ss.android.ugc.aweme.lite",
+                                                       control_id="m.l.live.plugin:id/more_toolbar_icon", timeout=5)
+                redis_client.set(device_id + "operate", action4_id)
+                redis_client.set(f"{device_id}_step4", "1")
+                redis_client.delete(f"{device_id}_step3")
+
+                loggerKit.info("设备:{0}, action7_id:{1}", device_id, action4_id)
+
+                return action6_dict
+
+            """
+            发送第4条指令
+            点开直播间对应的输入框
+            """
+            action6_dict = many_click_by_position(action4_id, target_app="douyin", target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  count=2, timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action4_id)
+
+            return action6_dict
+
+        # 第5步
+        # 点击分享
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '点击直播间右下角更多按钮失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击分享
+            """
+            action6_dict = single_click_by_text(action5_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="分享", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action5_id)
+
+            return action6_dict
+
+        # 第6步
+        # 点击复制链接
+        step4 = redis_client.get(f"{device_id}_step5")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '点击分享失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击分享
+            """
+            action6_dict = single_click_by_text(action5_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="复制链接", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action5_id)
+
+            return action6_dict
+
+        # 第7步
+        # 点击
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '点击搜索按钮/分享链接失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            点开直播间对应的输入框
+            """
+            action6_dict = single_click_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="m.l.live.plugin:id/edit_btn_audience", timeout=5)
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '唤醒评论输入框失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            如果没获取到对应的评论 任务结束 关闭
+            """
+
+            request_data = {
+                "title": "",
+                "comment": "",
+                "platform": "douyin",
+                "mode": "1"
+            }
+
+            loggerKit.info("任务id:{0},请求AIGC信息:{1}", task_id, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword, request_data, response)
+
+                reply = '不错👍'
+
+            response_body = json.loads(response.text)
+
+            reply_data = response_body.get('data')
+
+            reply = reply_data.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword, request_data, response)
+
+                reply = '不错👍'
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action9_id)
+
+            # 评论框赋值
+            action9_dict = send_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="m.l.live.plugin:id/edit_text_container",
+                                                content=reply, timeout=5, sleep_time=5, item_index=0)
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(last_action_id))
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '评论输入框赋值失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            发送评论
+            """
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="m.l.live.plugin:id/input_panel_send_view", timeout=5,
+                                                    sleep_time=5)
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            # 元素未找到
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is null"
+                }
+
+                del_key_vague(device_id)
+                callback_task(500, '发送评论失败', task_id, device_id, 0, None)
+                return return_dict
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            点赞直播间
+            """
+            like_count = random.randint(10, 15)
+            action11_dict = many_click_by_position(action11_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   count=like_count, timeout=5)
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        """
+        停止app
+        """
+        step10 = redis_client.get(f"{device_id}_step11")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            # 已完成任务数
+            done_comment_count = redis_client.incr(device_id + "done_count", 1)
+
+            # 预期完成任务数
+            anticipate_comment_count = redis_client.get(device_id + 'comment_count')
+
+            action12_id = int(round(time.time() * 1000))
+
+            # 如果已经达到预期评论次数 结束任务
+            if int(done_comment_count) >= int(anticipate_comment_count):
+                """
+                停止指令
+                停止app
+                """
+
+                action12_dict = stop_app(action12_id, target_app="douyin", target_version="28.8.0",
+                                         package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                del_key_vague(device_id)
+
+                loggerKit.info("设备:{0}, action11_id:{1}", device_id, action12_id)
+
+                # 回调任务中心修改任务状态
+                callback_task(None, None, task_id, device_id, 1, None)
+
+                return action12_dict
+
+            # 未达到评论次数 重新开始评论
+            """
+            发送第7条指令
+            点开直播间对应的输入框
+            """
+            action6_dict = single_click_by_control(action12_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="m.l.live.plugin:id/edit_btn_audience", timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action12_id)
+
+            return action6_dict
+
+
+
+
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")

+ 759 - 0
douyin/douyin_officail_account_interact.py

@@ -0,0 +1,759 @@
+"""
+官媒账号互动操作(点赞收藏评论 按账号去重)
+"""
+import time
+
+from urllib.parse import quote
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, start_app, get_content_by_control, single_click_by_text, \
+    many_click_by_position, stop_app, send_text_by_control, get_text_by_control, back_last_page, swipe_screen
+from tools import loggerKit, redis_client
+import json
+import httpx
+import threading
+import yaml
+import requests
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = extern_domain + config['bmp-content-center']['comment_local_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+# 官媒任务
+# 版本29.5.0
+def douyin_random_watch_video(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    # resource_name = inner_data.get("resourceName")
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    resource_name = extension_info['author']
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令被用户终止', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+            del_key_vague(device_id)
+            call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        # 获取到对应抖音账号并存入redis:
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取当前的抖音账号
+            获取当前抖音账号
+            """
+            action2_dict = get_text_by_control(action2_id,
+                                               control_id="com.ss.android.ugc.aweme.lite:id/ib7",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=3)
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step_get_account", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step1 = redis_client.get(f"{device_id}_step_get_account")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            # 获取当前登陆账号并存入缓存
+            account_name = result.get("performActionText")
+            if account_name is None:
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "未找到签到指示"
+                }
+
+                del_key_vague(device_id)
+                call_back_task_reply(500, '未获取到账号信息', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            # 将当前设备操作的账号存入redis
+            redis_client.set(device_id + 'account', account_name)
+
+            """
+            发送第2条指令
+            点击:关注
+            """
+            action2_dict = single_click_by_control(action2_id,
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/o3+",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step_get_account")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击:荣威ROEWE
+            """
+            action3_dict = get_content_by_control(action3_id, title=resource_name,
+                                                  target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step5 = redis_client.get(f"{device_id}_step4")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            随机点击某个视频
+            """
+            action6_dict = single_click_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/container",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/container",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action6_dict
+
+        # 获取对应的视频时间戳
+
+        step5 = redis_client.get(f"{device_id}_step6")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            插播指令 获取到对应视频的发布时间
+            
+            """
+            action6_dict = get_text_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/_+",
+                                               target_app="douyin",
+                                               target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               sleep_time=5)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step_timestamp", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action6_dict
+
+        # 判断该视频对应账号是否已经点赞过
+        step5 = redis_client.get(f"{device_id}_step_timestamp")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            timestamp = result.get("performActionText")
+
+            redis_client.set(device_id + 'timestamp', timestamp)
+
+            if timestamp is None or timestamp == '':
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+                del_key_vague(device_id)
+                call_back_task_reply(500, '任务执行失败,未获取到时间戳,无法判断是否执行过', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            """
+            根据发布时间和用户账号 判断该用户是否对该帖子进行过操作
+            未操作过开始进行点赞 已经操作过则下滑下一个
+
+            """
+            # redis中获取到对应账号
+            account = redis_client.get(device_id + 'account')
+
+            # 获取账号已做过的帖子集合
+            done_list = redis_client.lrange('douyin_done:' + account, 0, -1)
+
+            if done_list is None:
+                # 如果处理视频集合为空 表示未处理过视频 进行点赞收藏
+                action7_dict = many_click_by_position(action6_id, target_app="douyin",
+                                                      target_version="28.8.0",
+                                                      package_name="com.ss.android.ugc.aweme.lite")
+
+                redis_client.set(device_id + "operate", action6_id)
+                redis_client.set(f"{device_id}_step7", "1")
+                redis_client.delete(f"{device_id}_step_timestamp")
+                loggerKit.info("task:{0}账号{1}未对视频{2}进行过处理,开始进行点赞", task_id, account, timestamp)
+                return action7_dict
+
+            if timestamp in done_list:
+                # 添加滑动次数
+                swipe_count = redis_client.incr(device_id + "swipe_count", 1)
+                # 从0开始计数 滑动五次结束
+                if swipe_count >= 5:
+                    action7_dict = stop_app(action6_id, target_app="douyin", target_version="28.8.0",
+                                            package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+                    loggerKit.info("task:{0}账号{1}已对官媒{2}前五条视频做过处理", task_id, account, resource_name)
+
+                    # 回调任务中心修改任务状态
+                    call_back_task_reply(None, "账号:" + account + "已经对账号:" + resource_name + "前五条视频进行过处理", task_id,
+                                         device_id, 1, None, None)
+
+                    del_key_vague(device_id)
+
+                    return action7_dict
+
+                # 如果该视频已经在已操作集合中 进行下滑
+                action7_dict = swipe_screen(action6_id, scale=1,
+                                            target_app="douyin",
+                                            target_version="28.8.0",
+                                            package_name="com.ss.android.ugc.aweme.lite")
+
+                redis_client.set(device_id + "operate", action6_id)
+                redis_client.set(f"{device_id}_step6", "1")
+                redis_client.delete(f"{device_id}_step_timestamp")
+                loggerKit.info("task:{0}账号{1}已对视频{2}进行过处理,进行下滑操作", task_id, account, timestamp)
+
+                return action7_dict
+
+            action7_dict = single_click_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/cs5",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step_timestamp")
+            return action7_dict
+
+        # 视频收藏
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            视频收藏->长按收藏
+            """
+            action8_dict = single_click_by_control(action8_id, control_id="com.ss.android.ugc.aweme.lite:id/bnb",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 获取帖子标题
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            获取帖子标题
+            """
+            action9_dict = get_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/desc", timeout=5)
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            """
+            result_text = result.get("performActionText")
+
+            # if result_text is None:
+            #     return_dict = {
+            #         "data": "",
+            #         "code": -2,
+            #         "message": "fail, result_text is null"
+            #     }
+            #
+            #     # 回调任务中心
+            #
+            #     del_key_vague(device_id)
+            #
+            #     callback_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+            #
+            #     return return_dict
+
+            redis_client.set(device_id + "douyin" + "comments_title", result_text)
+
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + result_text, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, result_text, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, result_text, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "mode": "1",
+                "title": result_text,
+                "comment": existing_comment,
+                "platform": "douyin"
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, result_text, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                    timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            response_body = json.loads(response.text)
+
+            reply_data = response_body.get('data')
+
+            reply = reply_data.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(last_action_id))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            点击评论框
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action11_dict = single_click_by_control(action11_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/bq0", timeout=5)
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action12_id_mem:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action12_dict = send_text_by_control(action12_id, target_app="douyin", target_version="28.8.0",
+                                                 package_name="com.ss.android.ugc.aweme.lite",
+                                                 control_id="com.ss.android.ugc.aweme.lite:id/bq0",
+                                                 content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            comment_title = redis_client.get(device_id + "douyin" + "comments_title")
+
+            content_result = content_detail(resource_name, comment_title, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            发送评论
+            """
+            action12_dict = single_click_by_control(action13_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/bg=", timeout=5)
+            redis_client.set(device_id + "operate", action13_id)
+            redis_client.set(f"{device_id}_step13", "1")
+            redis_client.delete(f"{device_id}_step12")
+
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+        step13 = redis_client.get(f"{device_id}_step13")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action14_id = int(round(time.time() * 1000))
+
+            # 评论发送成功,将帖子表识存入到已处理过的集合中
+            account = redis_client.get(device_id + 'account')
+            title = redis_client.get(device_id + 'timestamp')
+
+            redis_client.left_push('douyin_done:' + account, title)
+            """
+            发送第14条指令
+            返回上一页
+            """
+            action14_dict = back_last_page(action14_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite", sleep_time=3)
+            redis_client.set(device_id + "operate", action14_id)
+            redis_client.set(f"{device_id}_step14", "1")
+            redis_client.delete(f"{device_id}_step13")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action14_id)
+
+            return action14_dict
+
+        step14 = redis_client.get(f"{device_id}_step14")
+        if step14 is not None and int(step14) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action15_id = int(round(time.time() * 1000))
+            """
+            发送第15条指令
+            浏览90s
+            """
+            action15_dict = many_click_by_position(action15_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite", count=1, sleep_time=90)
+            redis_client.set(device_id + "operate", action15_id)
+            redis_client.set(f"{device_id}_step15", "1")
+            redis_client.delete(f"{device_id}_step14")
+
+            loggerKit.info("设备:{0}, action15_id:{1}", device_id, action15_id)
+
+            return action15_dict
+
+        """
+        停止app
+        """
+        step15 = redis_client.get(f"{device_id}_step15")
+        if step15 is not None and int(step15) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action14_id_mem:{1}", device_id, int(last_action_id))
+
+            action16_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action14_dict = stop_app(action16_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action16_id)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            # 回调任务中心修改任务状态
+            call_back_task_reply(None, None, task_id, device_id, 1, None, reply_json)
+
+            del_key_vague(device_id)
+
+            return action14_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+def call_back_task_reply(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 279 - 0
douyin/douyin_random_search.py

@@ -0,0 +1,279 @@
+"""
+抖音极速版
+随机搜索
+"""
+import random
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import swipe_screen, single_click_by_control, send_text_by_control, single_click_by_text, \
+    continual_swipe_screen, stop_app, start_app
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+
+import json
+
+
+# 版本29.5.0
+def douyin_random_search(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    # keyword = inner_data.get("resourceName")
+    extension_info = data['data']['extendInfo']
+    extension_info = json.loads(extension_info)
+    keyword = extension_info['searchKey']
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            首页搜索关键词
+            """
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/fsf",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/fsf")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击搜索文本输入框
+            """
+            action2_dict = single_click_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/et_search_kw")
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            输入查询关键词
+            """
+            action3_dict = send_text_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击搜索
+            """
+            action4_dict = single_click_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nou",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/nou")
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+
+        # 切换到"视频"Tab页面
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            切换到"视频"Tab页面
+            """
+
+            action5_dict = single_click_by_text(action5_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="视频", timeout=20)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        # 点击第一个视频
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            点击第一个视频
+            """
+            action6_dict = single_click_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/ksq", timeout=20)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        # 下滑视频播放3分钟;3分钟内动作随机(仅下滑看视频动作)
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            下滑视频播放3分钟;3分钟内动作随机(仅下滑看视频动作)
+            """
+            action7_dict = continual_swipe_screen(action7_id, target_app="douyin", target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time=3 * 60)
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        # 停止
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            关闭
+            """
+            action8_dict = stop_app(action8_id, target_app="douyin", target_version="28.8.0",
+                                    package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action8_id)
+
+            del_key_vague(device_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action8_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

+ 661 - 0
douyin/douyin_random_watch_video.py

@@ -0,0 +1,661 @@
+"""
+随机浏览荣威官微下的视频
+
+抖音正常版
+"""
+import random
+import time
+
+from urllib.parse import quote
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, start_app, get_content_by_control, single_click_by_text, \
+    many_click_by_position, stop_app, continual_swipe_screen, send_text_by_control, get_text_by_control, back_last_page
+from tools import loggerKit, redis_client
+import json
+import httpx
+import threading
+import yaml
+import requests
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_library_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+def douyin_random_watch_video(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    resource_name = inner_data.get("resourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令被用户终止', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+            del_key_vague(device_id)
+            call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击:关注
+            """
+            action2_dict = single_click_by_control(action2_id,
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/od2",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击:荣威ROEWE
+            """
+            action3_dict = get_content_by_control(action3_id, title=resource_name,
+                                                  target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        # step3 = redis_client.get(f"{device_id}_step3")
+        # if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+        #         last_action_id):
+        #     loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+        #     action4_id = int(round(time.time() * 1000))
+        #     """
+        #     发送第4条指令
+        #     点击:作品
+        #     """
+        #     action4_dict = single_click_by_control(action4_id, control_id="com.ss.android.ugc.aweme.lite:id/g7h",
+        #                                            control_ids="com.ss.android.ugc.aweme.lite:id/g7h",
+        #                                            target_app="douyin",
+        #                                            target_version="28.8.0",
+        #                                            package_name="com.ss.android.ugc.aweme.lite")
+        #     redis_client.set(device_id + "operate", action4_id)
+        #     redis_client.set(f"{device_id}_step4", "1")
+        #     redis_client.delete(f"{device_id}_step3")
+        #
+        #     loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+        #
+        #     return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            上滑随机屏
+            """
+            random_scale = random.randint(2, 5)
+            action5_dict = continual_swipe_screen(action5_id, scale=random_scale,
+                                                  target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_count=random_scale)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            随机点击某个视频
+            """
+            action6_dict = single_click_by_control(action6_id, control_id="com.ss.android.ugc.aweme.lite:id/container",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/container",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            return action6_dict
+
+        # 视频点赞
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            视频点赞
+            """
+            action7_dict = many_click_by_position(action7_id, target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action7_dict
+
+        # 视频收藏
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            视频收藏->长按收藏
+            """
+            action8_dict = single_click_by_control(action8_id, control_id="com.ss.android.ugc.aweme.lite:id/bn=",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 获取帖子标题
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            获取帖子标题
+            """
+            action9_dict = get_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/desc", timeout=5)
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            """
+            result_text = result.get("performActionText")
+
+            # if result_text is None:
+            #     return_dict = {
+            #         "data": "",
+            #         "code": -2,
+            #         "message": "fail, result_text is null"
+            #     }
+            #
+            #     # 回调任务中心
+            #
+            #     del_key_vague(device_id)
+            #
+            #     callback_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+            #
+            #     return return_dict
+
+            redis_client.set(device_id + "douyin" + "comments_title", result_text)
+
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + result_text, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, result_text, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, result_text, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "mode": "1",
+                "content": result_text,
+                "existingComment": existing_comment
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, result_text, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                    timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            response_body = json.loads(response.text)
+
+            reply = response_body.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(last_action_id))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            点击评论框
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action11_dict = single_click_by_control(action11_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/brv", timeout=5)
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action12_id_mem:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action12_dict = send_text_by_control(action12_id, target_app="douyin", target_version="28.8.0",
+                                                 package_name="com.ss.android.ugc.aweme.lite",
+                                                 control_id="com.ss.android.ugc.aweme.lite:id/brv",
+                                                 content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            comment_title = redis_client.get(device_id + "douyin" + "comments_title")
+
+            content_result = content_detail(resource_name, comment_title, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            发送评论
+            """
+            action12_dict = single_click_by_control(action13_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/bcz", timeout=5)
+            redis_client.set(device_id + "operate", action13_id)
+            redis_client.set(f"{device_id}_step13", "1")
+            redis_client.delete(f"{device_id}_step12")
+
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+        step13 = redis_client.get(f"{device_id}_step13")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action14_id = int(round(time.time() * 1000))
+            """
+            发送第14条指令
+            返回上一页
+            """
+            action14_dict = back_last_page(action14_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite", sleep_time=3)
+            redis_client.set(device_id + "operate", action14_id)
+            redis_client.set(f"{device_id}_step14", "1")
+            redis_client.delete(f"{device_id}_step13")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action14_id)
+
+            return action14_dict
+
+        step14 = redis_client.get(f"{device_id}_step14")
+        if step14 is not None and int(step14) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action15_id = int(round(time.time() * 1000))
+            """
+            发送第15条指令
+            浏览90s
+            """
+            action15_dict = many_click_by_position(action15_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite", count=1, sleep_time=90)
+            redis_client.set(device_id + "operate", action15_id)
+            redis_client.set(f"{device_id}_step15", "1")
+            redis_client.delete(f"{device_id}_step14")
+
+            loggerKit.info("设备:{0}, action15_id:{1}", device_id, action15_id)
+
+            return action15_dict
+
+        """
+        停止app
+        """
+        step15 = redis_client.get(f"{device_id}_step15")
+        if step15 is not None and int(step15) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action14_id_mem:{1}", device_id, int(last_action_id))
+
+            action16_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action14_dict = stop_app(action16_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action16_id)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            # 回调任务中心修改任务状态
+            call_back_task_reply(None, None, task_id, device_id, 1, None, reply_json)
+
+            del_key_vague(device_id)
+
+            return action14_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+def call_back_task_reply(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 266 - 0
douyin/douyin_sign.py

@@ -0,0 +1,266 @@
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen, single_click_by_control, check_pic_exist, \
+    click_pic
+
+import requests
+import json
+import threading
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音极速版签到
+# version 28.8.0
+def douyin_spider(device_serial, mobile, task_id, keyword, media_channel, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, mobile, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result != "success":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当日已签到过', task_id, mobile, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击急速版抖音赚钱按键
+
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/hvy",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/hvy,"
+                                                               "com.ss.android.ugc.aweme.lite:id/lf3")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        ready_path = 'sign_ready.png'
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            判断签到按钮是否存在
+            """
+            sign_ready_base64 = pic_to_base64(ready_path)
+
+            action2_dict = check_pic_exist(action2_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite",
+                                           pic_base64=sign_ready_base64)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击去签到按钮
+            """
+            sign_ready_base64 = pic_to_base64(ready_path)
+
+            action3_dict = click_pic(action3_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite",
+                                           pic_base64=sign_ready_base64)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        sign_path = 'sign.png'
+
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击签到
+            """
+            sign_base64 = pic_to_base64(sign_path)
+
+            action3_dict = click_pic(action4_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite",
+                                     pic_base64=sign_base64)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        """
+        停止app
+        """
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action5_dict = stop_app(action5_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.delete(f"{device_id}_step4")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action5_dict)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, mobile, 1, None)
+
+            return action5_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+# post请求
+# def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+#     response = {
+#         "errorCode": error_code,
+#         "errorMsg": error_msg,
+#         "taskId": task_id,
+#         "taskStatus": task_status,
+#         "taskExecuteResponse": task_execute_response,
+#         "content": content,
+#         "demoFlag": demo_flag
+#     }
+#     return response
+#
+#
+# # 回调任务中心接口
+# def callback_task(err_code, err_msg, task_id, device_id, execute_status, result):
+#     callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, None, None)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}  。回调开始", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, task_callback_url)
+#     current_timeout = (5, 10)
+#     callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False))
+#     return callback_response2

+ 497 - 0
douyin/douyin_train_home_comment.py

@@ -0,0 +1,497 @@
+import random
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen, single_click_by_control, swipe_screen, \
+    get_text_by_control
+
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音养号首页帖子评论(浏览30-50帖子 随机时长 对其评论进行观看)
+# version 29.5.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            滑动15-20个视频 
+            """
+
+            swipe_count = random.randint(5, 10)
+
+            action1_dict = continual_swipe_screen(action1_id,
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time_interval='3,10',
+                                                  continuous_count=swipe_count)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("任务:{0}, action1_id,滑动15-20个视频", task_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+
+        # 获取评论数量
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            获取评论数量
+            """
+
+            action2_dict = get_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/bez", timeout=5,
+                                               item_index=0)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("任务:{0}, action2_id,获取评论数量", task_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+
+        # 判断是否有评论数
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            判断是否有评论数量
+            """
+            result_text = result.get("performActionText")
+            if result_text is None or result_text == '':
+                # 没有获取到评论数量,可能是在直播间页面。跳过此次点开评论操作
+                # 向下滑动一次,再重新获取评论
+                action2_dict = swipe_screen(action3_id,
+                                            package_name="com.ss.android.ugc.aweme.lite",
+                                            sleep_time=3)
+                redis_client.set(device_id + "operate", action3_id)
+                redis_client.delete(f"{device_id}_step2")
+                redis_client.set(f"{device_id}_step1", "1")
+
+                loggerKit.info("任务:{0}, action3_id,评论信息为:{1},不可点击再次滑动", task_id, result_text)
+
+                return action2_dict
+
+            elif result_text == '评论':
+                # 没有对应的评论次数,仅有评论文案 证明该帖子无评论。跳过此次点开评论操作
+                # 向下滑动一次,再重新获取评论
+                action2_dict = swipe_screen(action3_id,
+                                            package_name="com.ss.android.ugc.aweme.lite",
+                                            sleep_time=3)
+                redis_client.set(device_id + "operate", action3_id)
+                redis_client.delete(f"{device_id}_step2")
+                redis_client.set(f"{device_id}_step1", "1")
+
+                loggerKit.info("任务:{0}, action3_id,评论信息为:{1},不可点击再次滑动", task_id, result_text)
+
+                return action2_dict
+
+            else:
+                # 有对应评论,需要点击进去进行滑动
+                action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                       package_name="com.ss.android.ugc.aweme.lite",
+                                                       control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                       timeout=5)
+                redis_client.set(device_id + "operate", action3_id)
+                redis_client.set(f"{device_id}_step3", "1")
+                redis_client.delete(f"{device_id}_step2")
+
+                loggerKit.info("任务:{0}, action3_id,评论信息为:{1},点击评论框", task_id, result_text)
+
+                return action3_dict
+
+        # 进行滑动
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            进行滑动
+            """
+            action4_dict = swipe_screen(action4_id,
+                                        package_name="com.ss.android.ugc.aweme.lite",
+                                        sleep_time=8)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("任务:{0}, action4_id,向上滑动一次", task_id)
+
+            return action4_dict
+
+        # 关闭评论框
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            关闭评论框
+            """
+
+            action5_dict = single_click_by_control(action5_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/back_btn",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/back_btn,"
+                                                               "com.ss.android.ugc.aweme.lite:id/back",
+                                                   timeout=5)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("任务:{0}, action5_id,关闭评论", task_id)
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            滑动10-15个视频 
+            """
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+
+                del_key_vague(device_id)
+
+                callback_task(500, '任务执行失败,无法正常关闭评论框', task_id, device_id, 0, None)
+
+                return return_dict
+
+            swipe_count = random.randint(5, 10)
+
+            action6_dict = continual_swipe_screen(action6_id,
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time_interval='3,10',
+                                                  continuous_count=swipe_count)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("任务:{0}, action6_id,继续下滑10-15个视频", task_id)
+
+            return action6_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+
+        # 获取评论数量
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            获取评论数量
+            """
+
+            action7_dict = get_text_by_control(action7_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/bez", timeout=5,
+                                               item_index=0)
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("任务:{0}, action7_id,获取评论数量", task_id)
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+
+        # 判断是否有评论数
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            判断是否有评论数量
+            """
+            result_text = result.get("performActionText")
+            if result_text is None or result_text == '':
+                # 没有获取到评论数量,可能是在直播间页面。跳过此次点开评论操作
+                # 向下滑动一次,再重新获取评论
+                action8_dict = swipe_screen(action8_id,
+                                            package_name="com.ss.android.ugc.aweme.lite",
+                                            sleep_time=3)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.delete(f"{device_id}_step7")
+                redis_client.set(f"{device_id}_step6", "1")
+
+                loggerKit.info("任务:{0}, action8_id,获取评论数量为:{1},继续向下滑动", task_id, result_text)
+
+                return action8_dict
+
+            elif result_text == '评论':
+                # 没有对应的评论次数,仅有评论文案 证明该帖子无评论。跳过此次点开评论操作
+                # 向下滑动一次,再重新获取评论
+                action8_dict = swipe_screen(action8_id,
+                                            package_name="com.ss.android.ugc.aweme.lite",
+                                            sleep_time=3)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.delete(f"{device_id}_step7")
+                redis_client.set(f"{device_id}_step6", "1")
+
+                loggerKit.info("任务:{0}, action8_id,获取评论数量为:{1},继续向下滑动", task_id, result_text)
+
+                return action8_dict
+
+            else:
+                # 有对应评论,需要点击进去进行滑动
+                action8_dict = single_click_by_control(action8_id, target_app="douyin", target_version="28.8.0",
+                                                       package_name="com.ss.android.ugc.aweme.lite",
+                                                       control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                       timeout=5)
+                redis_client.set(device_id + "operate", action8_id)
+                redis_client.set(f"{device_id}_step8", "1")
+                redis_client.delete(f"{device_id}_step7")
+
+                loggerKit.info("任务:{0}, action8_id,获取评论数量为:{1},点开评论", task_id, result_text)
+
+                return action8_dict
+
+        # 进行滑动
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            进行滑动
+            """
+            action9_dict = swipe_screen(action9_id,
+                                        package_name="com.ss.android.ugc.aweme.lite",
+                                        sleep_time=8)
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            loggerKit.info("任务:{0}, action9_id,滑动评论", task_id)
+
+            return action9_dict
+
+        # 关闭评论框
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(last_action_id))
+
+            if perform_action_result == "invalid operation":
+                # 回调任务中心
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, performActionResult is not success"
+                }
+
+                del_key_vague(device_id)
+
+                callback_task(500, '任务执行失败,无法正常关闭评论框', task_id, device_id, 0, None)
+
+                return return_dict
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            关闭评论框
+            """
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/back_btn",
+                                                    control_ids="com.ss.android.ugc.aweme.lite:id/back_btn,"
+                                                                "com.ss.android.ugc.aweme.lite:id/back",
+                                                    timeout=5)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("任务:{0}, action10_id,关闭评论", task_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            滑动10-15个视频 
+            """
+
+            swipe_count = random.randint(5, 10)
+
+            action11_dict = continual_swipe_screen(action11_id,
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   continuous_time_interval='3,10',
+                                                   continuous_count=swipe_count)
+
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("任务:{0}, action11_id,继续滑动10-15", task_id)
+
+            return action11_dict
+
+        # 停止
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            关闭
+            """
+            action12_dict = stop_app(action12_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            # redis_client.set(device_id + "operate", action12_id)
+            # redis_client.set(f"{device_id}_step12", "1")
+            # redis_client.delete(f"{device_id}_step11")
+
+            loggerKit.info("任务:{0}, action12_id,停止app", task_id)
+
+            del_key_vague(device_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action12_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")

+ 348 - 0
douyin/douyin_train_home_page.py

@@ -0,0 +1,348 @@
+import random
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen, single_click_by_control
+
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音养号首页(浏览30-50视频 随机时长和点赞收藏)
+# version 29.5.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            滑动10-15个视频 
+            """
+
+            swipe_count = random.randint(5, 10)
+
+            action1_dict = continual_swipe_screen(action1_id,
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time_interval='3,10',
+                                                  continuous_count=swipe_count)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+
+        # 进行点赞
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            进行点赞
+            """
+
+            action2_dict = single_click_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/cs5", timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        # 进行收藏
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            进行收藏
+            """
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bnb", timeout=5)
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            滑动10-15个视频 
+            """
+
+            swipe_count = random.randint(5, 10)
+
+            action4_dict = continual_swipe_screen(action4_id,
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time_interval='3,10',
+                                                  continuous_count=swipe_count)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+
+        # 进行点赞
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            进行点赞
+            """
+
+            action5_dict = single_click_by_control(action5_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/cs5", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        # 进行收藏
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            进行收藏
+            """
+            action6_dict = single_click_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bnb", timeout=5)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            滑动10-15个视频 
+            """
+
+            swipe_count = random.randint(5, 10)
+
+            action7_dict = continual_swipe_screen(action7_id,
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time_interval='3,10',
+                                                  continuous_count=swipe_count)
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+
+        # 进行点赞
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            进行点赞
+            """
+
+            action8_dict = single_click_by_control(action8_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/cs5", timeout=5)
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action8_id)
+
+            return action8_dict
+
+        # 进行收藏
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            进行收藏
+            """
+            action9_dict = single_click_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bnb", timeout=5)
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_id)
+
+            return action9_dict
+
+        # 停止
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            关闭
+            """
+            action10_dict = stop_app(action10_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            del_key_vague(device_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+            return action10_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="29.5.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")

+ 548 - 0
douyin/douyin_user_operate.py

@@ -0,0 +1,548 @@
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, single_click_by_control, \
+    send_text_by_control, single_click_by_text, get_text_by_control, click_pic
+
+from tools.pic_base64_util import pic_to_base64
+
+import requests
+import json
+import threading
+import yaml
+
+import httpx
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_url']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音极速版用户操作(对指定用户作品进行点赞 评论和收藏)
+# version 28.8.0
+def douyin_spider(task_id, keyword, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result != "success":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当日已签到过', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击急速版抖音搜索按钮
+
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/hvy",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/hvy,"
+                                                               "com.ss.android.ugc.aweme.lite:id/lf3")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            对搜索框进行赋值
+            """
+
+            action2_dict = send_text_by_control(action2_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                control_id="com.ss.android.ugc.aweme.lite:id/et_search_kw",
+                                                content=keyword, timeout=5)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击搜索按钮 根据图片识别
+            """
+
+            action3_dict = single_click_by_control(action3_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/nup")
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击用户按钮
+            """
+            action4_dict = single_click_by_text(action4_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="用户", timeout=5)
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            点击用户tab下的第一个用户
+            """
+            action5_dict = single_click_by_control(action5_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   item_index=0,
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/gzs", timeout=5)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            进入用户对应的视频
+            """
+            action6_dict = single_click_by_control(action6_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/hld", timeout=5)
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_id)
+
+            return action6_dict
+
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(last_action_id))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            进行点赞
+            """
+            action7_dict = single_click_by_control(action7_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/cug", timeout=5)
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            收藏
+            """
+            action8_dict = single_click_by_control(action8_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/bn=", timeout=5)
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_dict)
+
+            return action8_dict
+
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action9_id_mem:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            获取到用户的帖子内容
+            """
+            action9_dict = get_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme.lite:id/desc", timeout=5)
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_id)
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            """
+            result_text = result.get("performActionText")
+
+            if result_text is None:
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+
+                # 回调任务中心
+
+                del_key_vague(device_id)
+
+                callback_task(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+
+                return return_dict
+
+            request_data = {
+                "templateId": "dcd_comment",
+                "content": result_text,
+                "existingComment": None
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, keyword, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/comment_container",
+                                                    timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            response_body = json.loads(response.text)
+
+            data = response_body.get('data')
+
+            reply_text = data.get('answer')
+
+            reply_text_json = json.loads(reply_text)
+
+            reply = reply_text_json.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, keyword, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(last_action_id))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            点击评论框
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                callback_task(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+
+                return return_dict
+
+            action11_dict = single_click_by_control(action11_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/brv", timeout=5)
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action12_id_mem:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                callback_task(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+
+                return return_dict
+
+            action12_dict = send_text_by_control(action12_id, target_app="douyin", target_version="28.8.0",
+                                                 package_name="com.ss.android.ugc.aweme.lite",
+                                                 control_id="com.ss.android.ugc.aweme.lite:id/brv",
+                                                 content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            发送评论
+            """
+            action12_dict = single_click_by_control(action13_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme.lite:id/bcw", timeout=5)
+            redis_client.set(device_id + "operate", action13_id)
+            redis_client.set(f"{device_id}_step13", "1")
+            redis_client.delete(f"{device_id}_step12")
+
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+        """
+        停止app
+        """
+        step13 = redis_client.get(f"{device_id}_step13")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action14_id_mem:{1}", device_id, int(last_action_id))
+
+            action14_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action14_dict = stop_app(action14_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite", timeout=5)
+
+            redis_client.delete(f"{device_id}_step4")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action14_id)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action14_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+# post请求
+# def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+#     response = {
+#         "errorCode": error_code,
+#         "errorMsg": error_msg,
+#         "taskId": task_id,
+#         "taskStatus": task_status,
+#         "taskExecuteResponse": task_execute_response,
+#         "content": content,
+#         "demoFlag": demo_flag
+#     }
+#     return response
+#
+#
+# # 回调任务中心接口
+# def callback_task(err_code, err_msg, task_id, device_id, execute_status, result):
+#     callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, None, None)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}  。回调开始", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, task_callback_url)
+#     current_timeout = (5, 10)
+#     callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False))
+#     return callback_response2

+ 297 - 0
douyin/douyin_watch_advise.py

@@ -0,0 +1,297 @@
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen, single_click_by_control, \
+    check_pic_exist, \
+    click_pic, get_content_by_control, swipe_screen
+
+import requests
+import json
+import threading
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音极速版看广告
+# version 28.8.0
+def douyin_spider(device_serial, task_id, keyword, media_channel, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "picNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result != "success":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击急速版抖音赚钱按键
+
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/hvy",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/hvy,"
+                                                               "com.ss.android.ugc.aweme.lite:id/lf3")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+
+        #  进入看广告任务标识
+        ready_advise_path = 'ready_advise.png'
+
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            查找进入看广告标识图片 向下最多滑动三次获取
+            """
+            sign_ready_base64 = pic_to_base64(ready_advise_path)
+
+            action2_dict = check_pic_exist(action2_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite",
+                                           pic_base64=sign_ready_base64, swipe_count=3)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击看广告标识 进行看广告动作
+            """
+            sign_ready_base64 = pic_to_base64(ready_advise_path)
+
+            action3_dict = click_pic(action3_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite",
+                                     pic_base64=sign_ready_base64)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            判断是否有返回元素    某些账号观看广告是下滑瀑布流(小o2024)需要进行下滑处理
+            """
+
+            action3_dict = get_content_by_control(action4_id, target_app="douyin", target_version="28.8.0",
+                                                  max_page="1",
+                                                  package_name="com.ss.android.ugc.aweme.lite", operator_type=1,
+                                                  title="返回")
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action3_dict
+
+        """
+        根据返回 进行持续下滑或不操作
+        """
+        step4 = redis_client.get(f"{device_id}_step4")
+
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            判断是否有返回元素    某些账号观看广告是下滑瀑布流(小o2024)需要进行下滑处理
+            """
+
+            if perform_action_result == 'eleNotFound':
+                action3_dict = swipe_screen(action5_id,
+                                            package_name="com.ss.android.ugc.aweme.lite", sleep_time=70)
+            else:
+                action3_dict = continual_swipe_screen(action5_id,
+                                                      package_name="com.ss.android.ugc.aweme.lite",
+                                                      continuous_time=90)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action3_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action5_dict = stop_app(action5_id, target_app="douyin", target_version="28.8.0",
+                                    package_name="com.ss.android.ugc.aweme.lite")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action5_dict)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action5_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+# post请求
+# def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+#     response = {
+#         "errorCode": error_code,
+#         "errorMsg": error_msg,
+#         "taskId": task_id,
+#         "taskStatus": task_status,
+#         "taskExecuteResponse": task_execute_response,
+#         "content": content,
+#         "demoFlag": demo_flag
+#     }
+#     return response
+#
+#
+# # 回调任务中心接口
+# def callback_task(err_code, err_msg, task_id, device_id, execute_status, result):
+#     callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, None, None)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}  。回调开始", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, task_callback_url)
+#     current_timeout = (5, 10)
+#     callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False))
+#     return callback_response2

+ 407 - 0
douyin/douyin_watch_noval.py

@@ -0,0 +1,407 @@
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from tools.pic_base64_util import pic_to_base64
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen, single_click_by_control, \
+    check_pic_exist, \
+    click_pic, get_content_by_control, swipe_screen, single_click_by_text
+
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音极速版看小说
+# version 28.8.0
+def douyin_spider(device_serial, task_id, keyword, media_channel, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,未达到看小说任务领取金币要求', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败,可能当前无法再次执行看广告任务', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击急速版抖音赚钱按键
+
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="com.ss.android.ugc.aweme.lite:id/hvy",
+                                                   control_ids="com.ss.android.ugc.aweme.lite:id/hvy,"
+                                                               "com.ss.android.ugc.aweme.lite:id/lf3")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+
+        # 看小说地址
+        noval_path = 'noval.png'
+
+        # 检查是否存在看小说按钮
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action1_id_mem:{1}", device_id, int(last_action_id))
+
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            检查是否有看小说图片
+            """
+            noval_base64 = pic_to_base64(noval_path)
+
+            action2_dict = check_pic_exist(action2_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite",
+                                           pic_base64=noval_base64, swipe_count=3)
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+
+            loggerKit.info("设备:{0}, action2_id:{1}", device_id, action2_id)
+
+            return action2_dict
+
+        # 点击看小说按钮
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击小说按钮
+            """
+            noval_base64 = pic_to_base64(noval_path)
+
+            action3_dict = click_pic(action3_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite",
+                                     pic_base64=noval_base64)
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+
+            loggerKit.info("设备:{0}, action3_id:{1}", device_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+
+        # 进入排行榜
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action3_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击更多进入排行榜
+            """
+
+            action4_dict = single_click_by_text(action4_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="更多", timeout=10, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("设备:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        # 阅读排行第一小说
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            进入排行第一小说
+            """
+
+            action5_dict = single_click_by_text(action5_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="1", timeout=10, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        # 阅读排行第一小说
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            进入排行第一小说
+            """
+
+            action5_dict = single_click_by_text(action5_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="1", timeout=10, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action5_id:{1}", device_id, action5_id)
+
+            return action5_dict
+
+        # 向右滑动五分钟每次间隔10s
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action5_id_mem:{1}", device_id, int(last_action_id))
+
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            向右滑动五分钟每次间隔8s
+            """
+
+            action6_dict = continual_swipe_screen(action6_id,
+                                                  package_name="com.ss.android.ugc.aweme.lite",
+                                                  sleep_time=10,
+                                                  continuous_time=60 * 5)
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action6_dict)
+
+            return action6_dict
+
+        # 点击右上方小金币 领取金币
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action6_id_mem:{1}", device_id, int(last_action_id))
+
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            点击小金币
+            """
+
+            action7_dict = single_click_by_control(action7_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite",
+                                                   control_id="m.l.novel:id/novel_sdk_ug_reader_task_icon")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action6_id:{1}", device_id, action7_dict)
+
+            return action7_dict
+
+        # 点击右上方小金币 领取金币
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action7_id_mem:{1}", device_id, int(last_action_id))
+
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            点击小金币
+            """
+
+            action8_dict = single_click_by_text(action8_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme.lite",
+                                                text="去领取", timeout=10, sleep_time=5)
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_dict)
+
+            return action8_dict
+
+        # 关闭抖音
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action8_id_mem:{1}", device_id, int(last_action_id))
+
+            action9_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action9_dict = stop_app(action9_id, target_app="douyin", target_version="28.8.0",
+                                    package_name="com.ss.android.ugc.aweme.lite")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action9_id:{1}", device_id, action9_dict)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action9_dict
+
+
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+# 批量模糊删除缓存
+# def del_key_vague(device_id):
+#     # 批量模糊删除keys
+#     keys = redis_client.match_pattern_prefix(device_id)
+#     if len(keys) > 0:
+#         # 需要判断是否有匹配的值, 没有的话会报错
+#         for key in keys:
+#             redis_client.delete(key)
+#         loggerKit.info(f"clear {device_id} keys success...")
+#     else:
+#         loggerKit.info(f"{device_id} keys none ...")
+
+
+# post请求
+# def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+#     response = {
+#         "errorCode": error_code,
+#         "errorMsg": error_msg,
+#         "taskId": task_id,
+#         "taskStatus": task_status,
+#         "taskExecuteResponse": task_execute_response,
+#         "content": content,
+#         "demoFlag": demo_flag
+#     }
+#     return response
+#
+#
+# # 回调任务中心接口
+# def callback_task(err_code, err_msg, task_id, device_id, execute_status, result):
+#     callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, None, None)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}  。回调开始", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, task_callback_url)
+#     current_timeout = (5, 10)
+#     callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False))
+#     return callback_response2

+ 661 - 0
douyin/fast_random_watch_video.py

@@ -0,0 +1,661 @@
+"""
+随机浏览荣威官微下的视频
+
+抖音极速版
+"""
+import random
+import time
+
+from urllib.parse import quote
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import single_click_by_control, start_app, get_content_by_control, single_click_by_text, \
+    many_click_by_position, stop_app, continual_swipe_screen, send_text_by_control, get_text_by_control, back_last_page
+from tools import loggerKit, redis_client
+import json
+import httpx
+import threading
+import yaml
+import requests
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+# 调用AIGC接口
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_library_url']
+
+get_commented_list_url = extern_domain + config['bmp-cp']['get_commented_list_url']
+
+
+def douyin_random_watch_video(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+    resource_name = inner_data.get("resourceName")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令执行失败', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '指令被用户终止', task_id, device_id, 0, None, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            call_back_task_reply(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+            del_key_vague(device_id)
+            call_back_task_reply(500, '任务执行失败,元素未找到', task_id, device_id, 0, None, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_text(action1_id, target_app="douyin", target_version="28.8.0",
+                                                package_name="com.ss.android.ugc.aweme",
+                                                text="我")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击:关注
+            """
+            action2_dict = single_click_by_control(action2_id,
+                                                   control_id="com.ss.android.ugc.aweme:id/zpn",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击:荣威ROEWE
+            """
+            action3_dict = get_content_by_control(action3_id, title=resource_name,
+                                                  target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            点击:作品
+            """
+            action4_dict = single_click_by_control(action4_id, control_id="com.ss.android.ugc.aweme:id/mv1",
+                                                   control_ids="com.ss.android.ugc.aweme:id/mv1",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme")
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            loggerKit.info("taskId:{0}, action4_id:{1}", device_id, action4_id)
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            上滑随机屏
+            """
+            random_scale = random.randint(2, 5)
+            action5_dict = continual_swipe_screen(action5_id, scale=random_scale,
+                                                  target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme",
+                                                  continuous_count=random_scale)
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action5_dict
+
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            随机点击某个视频
+            """
+            action6_dict = single_click_by_control(action6_id, control_id="com.ss.android.ugc.aweme:id/container",
+                                                   control_ids="com.ss.android.ugc.aweme:id/container",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            return action6_dict
+
+        # 视频点赞
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第7条指令
+            视频点赞
+            """
+            action7_dict = many_click_by_position(action7_id, target_app="douyin",
+                                                  target_version="28.8.0",
+                                                  package_name="com.ss.android.ugc.aweme")
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            return action7_dict
+
+        # 视频收藏
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            视频收藏->长按收藏
+            """
+            action8_dict = single_click_by_control(action8_id, control_id="com.ss.android.ugc.aweme:id/ct_",
+                                                   target_app="douyin",
+                                                   target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme")
+
+            redis_client.set(device_id + "operate", action8_id)
+            redis_client.set(f"{device_id}_step8", "1")
+            redis_client.delete(f"{device_id}_step7")
+
+            return action8_dict
+
+        # 获取帖子标题
+        step8 = redis_client.get(f"{device_id}_step8")
+        if step8 is not None and int(step8) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action9_id = int(round(time.time() * 1000))
+            """
+            发送第9条指令
+            获取帖子标题
+            """
+            action9_dict = get_text_by_control(action9_id, target_app="douyin", target_version="28.8.0",
+                                               package_name="com.ss.android.ugc.aweme.lite",
+                                               max_page='1',
+                                               control_id="com.ss.android.ugc.aweme:id/desc", timeout=5)
+
+            redis_client.set(device_id + "operate", action9_id)
+            redis_client.set(f"{device_id}_step9", "1")
+            redis_client.delete(f"{device_id}_step8")
+
+            return action9_dict
+
+        step9 = redis_client.get(f"{device_id}_step9")
+        if step9 is not None and int(step9) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action4_id_mem:{1}", device_id, int(last_action_id))
+
+            action10_id = int(round(time.time() * 1000))
+            """
+            发送第10条指令
+            根据视频内容 生成评论文案将文案缓存 并且点击评论
+            """
+            result_text = result.get("performActionText")
+
+            # if result_text is None:
+            #     return_dict = {
+            #         "data": "",
+            #         "code": -2,
+            #         "message": "fail, result_text is null"
+            #     }
+            #
+            #     # 回调任务中心
+            #
+            #     del_key_vague(device_id)
+            #
+            #     callback_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None)
+            #
+            #     return return_dict
+
+            redis_client.set(device_id + "douyin" + "comments_title", result_text)
+
+            comment_response = httpx.get(get_commented_list_url + '?resourceName=' + result_text, timeout=120)
+
+            # 评论实体
+            comment_body = json.loads(comment_response.text)
+
+            # 评论集合
+            comment_list = comment_body.get('data')
+
+            loggerKit.info("任务id:{0}对应标题:{1},已有评论:{2}", task_id, result_text, comment_list)
+
+            existing_comment = ''
+
+            if comment_list is not None:
+                comment_list = list(set(comment_list))
+
+                existing_comment = '||'.join(comment_list)
+
+            loggerKit.info("任务id:{0}对应标题:{1},拼接后的评论集合:{2}", task_id, result_text, existing_comment)
+            existing_comment = quote(existing_comment)
+
+            request_data = {
+                "mode": "1",
+                "content": result_text,
+                "existingComment": existing_comment
+            }
+
+            loggerKit.info("任务id:{0}对应标题:{1},请求AIGC信息:{2}", task_id, result_text, request_data)
+            response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+            # 点击评论框
+            action10_dict = single_click_by_control(action10_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme:id/comment_container",
+                                                    timeout=5)
+
+            if not response.is_success:
+                #  调用AIGC获取评论失败
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            response_body = json.loads(response.text)
+
+            reply = response_body.get('comment')
+
+            if '内容太少' in reply or '对不起' in reply or reply is None or reply == '' or '提供' in reply:
+                #  获取到的评论内容不符合
+                loggerKit.info("任务id:{0}对应标题:{1},请求AIGC失败信息:{2},返回信息:{3}", task_id, result_text, request_data, response)
+
+                redis_client.set(device_id + "douyin" + "comments", '不错👍')
+
+                redis_client.set(device_id + "operate", action10_id)
+                redis_client.set(f"{device_id}_step10", "1")
+                redis_client.delete(f"{device_id}_step9")
+
+                loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+                return action10_dict
+
+            # 将需要评论的内容存入缓存
+            redis_client.set(device_id + "douyin" + "comments", reply)
+
+            redis_client.set(device_id + "operate", action10_id)
+            redis_client.set(f"{device_id}_step10", "1")
+            redis_client.delete(f"{device_id}_step9")
+
+            loggerKit.info("设备:{0}, action10_id:{1}", device_id, action10_id)
+
+            return action10_dict
+
+        step10 = redis_client.get(f"{device_id}_step10")
+        if step10 is not None and int(step10) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action11_id_mem:{1}", device_id, int(last_action_id))
+
+            action11_id = int(round(time.time() * 1000))
+            """
+            发送第11条指令
+            点击评论框
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action11_dict = single_click_by_control(action11_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme:id/cyx", timeout=5)
+            redis_client.set(device_id + "operate", action11_id)
+            redis_client.set(f"{device_id}_step11", "1")
+            redis_client.delete(f"{device_id}_step10")
+
+            loggerKit.info("设备:{0}, action11_id:{1}", device_id, action11_id)
+
+            return action11_dict
+
+        step11 = redis_client.get(f"{device_id}_step11")
+        if step11 is not None and int(step11) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action12_id_mem:{1}", device_id, int(last_action_id))
+
+            action12_id = int(round(time.time() * 1000))
+            """
+            发送第12条指令
+            对评论框进行赋值
+            """
+            reply = redis_client.get(device_id + "douyin" + "comments")
+            if reply is None:
+                #  调用AIGC获取评论失败
+                del_key_vague(device_id)
+                return_dict = {
+                    "data": "",
+                    "code": -2,
+                    "message": "fail, result_text is null"
+                }
+                call_back_task_reply(500, '获取到的内容标题为null,无法评论', task_id, device_id, 0, None, None)
+
+                return return_dict
+
+            action12_dict = send_text_by_control(action12_id, target_app="douyin", target_version="28.8.0",
+                                                 package_name="com.ss.android.ugc.aweme.lite",
+                                                 control_id="com.ss.android.ugc.aweme:id/cyx",
+                                                 content=reply, timeout=5)
+            redis_client.set(device_id + "operate", action12_id)
+            redis_client.set(f"{device_id}_step12", "1")
+            redis_client.delete(f"{device_id}_step11")
+
+            comment_title = redis_client.get(device_id + "douyin" + "comments_title")
+
+            content_result = content_detail(resource_name, comment_title, reply, None)
+
+            redis_client.set(device_id + "reply_json", json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_id)
+
+            return action12_dict
+
+        step12 = redis_client.get(f"{device_id}_step12")
+        if step12 is not None and int(step12) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action13_id = int(round(time.time() * 1000))
+            """
+            发送第13条指令
+            发送评论
+            """
+            action12_dict = single_click_by_control(action13_id, target_app="douyin", target_version="28.8.0",
+                                                    package_name="com.ss.android.ugc.aweme.lite",
+                                                    control_id="com.ss.android.ugc.aweme:id/c2c", timeout=5)
+            redis_client.set(device_id + "operate", action13_id)
+            redis_client.set(f"{device_id}_step13", "1")
+            redis_client.delete(f"{device_id}_step12")
+
+            loggerKit.info("设备:{0}, action13_id:{1}", device_id, action12_dict)
+
+            return action12_dict
+
+        step13 = redis_client.get(f"{device_id}_step13")
+        if step13 is not None and int(step13) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action14_id = int(round(time.time() * 1000))
+            """
+            发送第14条指令
+            发送评论
+            """
+            action14_dict = back_last_page(action14_id, target_app="douyin", target_version="28.8.0",
+                                           package_name="com.ss.android.ugc.aweme.lite", sleep_time=3)
+            redis_client.set(device_id + "operate", action14_id)
+            redis_client.set(f"{device_id}_step14", "1")
+            redis_client.delete(f"{device_id}_step13")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action14_id)
+
+            return action14_dict
+
+        step14 = redis_client.get(f"{device_id}_step14")
+        if step14 is not None and int(step14) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action13_id_mem:{1}", device_id, int(last_action_id))
+
+            action15_id = int(round(time.time() * 1000))
+            """
+            发送第15条指令
+            发送评论
+            """
+            action15_dict = many_click_by_position(action15_id, target_app="douyin", target_version="28.8.0",
+                                                   package_name="com.ss.android.ugc.aweme.lite", count=1, sleep_time=90)
+            redis_client.set(device_id + "operate", action15_id)
+            redis_client.set(f"{device_id}_step15", "1")
+            redis_client.delete(f"{device_id}_step14")
+
+            loggerKit.info("设备:{0}, action15_id:{1}", device_id, action15_id)
+
+            return action15_dict
+
+        """
+        停止app
+        """
+        step15 = redis_client.get(f"{device_id}_step15")
+        if step15 is not None and int(step15) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action14_id_mem:{1}", device_id, int(last_action_id))
+
+            action16_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action14_dict = stop_app(action16_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme", timeout=5)
+
+            redis_client.delete(f"{device_id}_step4")
+
+            loggerKit.info("设备:{0}, action14_id:{1}", device_id, action16_id)
+
+            reply_json = redis_client.get(device_id + "reply_json")
+
+            # 回调任务中心修改任务状态
+            call_back_task_reply(None, None, task_id, device_id, 1, None, reply_json)
+
+            del_key_vague(device_id)
+
+            return action14_dict
+
+    else:
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+def call_back_task_reply(err_code, err_msg, task_id, device_id, execute_status, result, content):
+    callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, content, None)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}, 执行状态:{5}, requestJson:{6}  。回调开始",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, task_callback_url, execute_status, callback_request2)
+    current_timeout = (5, 10)
+    callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+    loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}, 执行状态:{5}, result:{6}",
+                   threading.current_thread().name,
+                   threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False),
+                   execute_status, result)
+    return callback_response2
+
+
+"""
+添加了其他参数
+"""
+
+
+# post请求
+def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+    response = {
+        "errorCode": error_code,
+        "errorMsg": error_msg,
+        "taskId": task_id,
+        "taskStatus": task_status,
+        "taskExecuteResponse": task_execute_response,
+        "content": content,
+        "demoFlag": demo_flag
+    }
+    return response
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }

+ 163 - 0
douyin/simple_browser_video.py

@@ -0,0 +1,163 @@
+"""
+简单刷视频
+"""
+
+import time
+
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+from scene.oprator.atom_data import start_app, stop_app, continual_swipe_screen
+
+import yaml
+
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+extern_domain = config['bmp-cp']['extern_domain']
+
+# 任务执行回调url
+task_callback_url = extern_domain + config['bmp-cp']['task_callback_url']
+
+
+# 抖音急速版根据刷视频获取奖金
+# version 28.8.0
+def douyin_spider(device_serial, task_id, keyword, media_channel, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        if perform_action_result != "success":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+
+            callback_task(500, '任务执行失败', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 每次操作完成后会将对应的操作唯一id存储到redis,并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        last_action_id = redis_client.get(device_id + "operate")
+
+        step0 = redis_client.get(f"{device_id}_step0")
+        if step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id) \
+                and perform_action_result == "success":
+            loggerKit.info("设备:{0}, action2_id_mem:{1}", device_id, int(last_action_id))
+
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            连续滑动视频
+            """
+            action3_dict = continual_swipe_screen(action1_id, package_name="com.ss.android.ugc.aweme.lite",
+                                                  continuous_time=60)
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("设备:{0}, action1_id:{1}", device_id, action1_id)
+
+            return action3_dict
+
+        """
+        停止app
+        """
+        step2 = redis_client.get(f"{device_id}_step1")
+        if (step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(last_action_id)
+                and perform_action_result == "success"):
+            loggerKit.info("设备:{0}, action10_id_mem:{1}", device_id, int(last_action_id))
+
+            action4_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action12_dict = stop_app(action4_id, target_app="douyin", target_version="28.8.0",
+                                     package_name="com.ss.android.ugc.aweme.lite")
+
+            redis_client.delete(f"{device_id}_step1")
+
+            del_key_vague(device_id)
+
+            loggerKit.info("设备:{0}, action12_id:{1}", device_id, action12_dict)
+
+            # 回调任务中心修改任务状态
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action12_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="douyin", target_version="28.8.0",
+                                 package_name="com.ss.android.ugc.aweme.lite")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step9")
+
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict
+
+
+# post请求
+# def call_back_request(error_code, error_msg, task_id, task_status, task_execute_response, content, demo_flag):
+#     response = {
+#         "errorCode": error_code,
+#         "errorMsg": error_msg,
+#         "taskId": task_id,
+#         "taskStatus": task_status,
+#         "taskExecuteResponse": task_execute_response,
+#         "content": content,
+#         "demoFlag": demo_flag
+#     }
+#     return response
+#
+#
+# # 回调任务中心接口
+# def callback_task(err_code, err_msg, task_id, device_id, execute_status, result):
+#     callback_request2 = call_back_request(err_code, err_msg, task_id, execute_status, result, None, None)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2}, 设备号:{3}, url:{4}  。回调开始", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, task_callback_url)
+#     current_timeout = (5, 10)
+#     callback_response2 = requests.post(task_callback_url, json=callback_request2, timeout=current_timeout)
+#     loggerKit.info("thread[{0}=>{1}], taskId:{2},设备号:{3}。回调结束:{4}", threading.current_thread().name,
+#                    threading.get_ident(), task_id, device_id, json.dumps(callback_response2.text, ensure_ascii=False))
+#     return callback_response2

BIN
douyin_search.png


+ 0 - 0
func/__init__.py


BIN
func/__pycache__/__init__.cpython-311.pyc


BIN
func/__pycache__/action_func.cpython-311.pyc


BIN
func/__pycache__/action_tool.cpython-311.pyc


+ 0 - 0
func/action_enum.py


+ 14 - 0
func/action_func.py

@@ -0,0 +1,14 @@
+# 批量模糊删除缓存
+from tools import redis_client, loggerKit
+
+
+def del_key_vague(device_id):
+    # 批量模糊删除keys
+    keys = redis_client.match_pattern_prefix(device_id)
+    if len(keys) > 0:
+        # 需要判断是否有匹配的值, 没有的话会报错
+        for key in keys:
+            redis_client.delete(key)
+        loggerKit.info(f"clear {device_id} keys success...")
+    else:
+        loggerKit.info(f"{device_id} keys none ...")

+ 785 - 0
func/action_tool.py

@@ -0,0 +1,785 @@
+import base64
+import os
+import time
+from func.action_func import del_key_vague
+from task.task_job import callback_task
+from tools import loggerKit, redis_client
+import scene.oprator.atom_data
+from tools.pic_base64_util import pic_to_base64
+
+"""
+检查任务状态
+:param task_id 任务id
+:param data 客户端请求的参数字典
+:param target_app app标识
+:param package_name APP id
+:param target_version APP 版本
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def check_result_status(
+        task_id,
+        data,
+        target_app,
+        target_version,
+        package_name,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取 指令结构体
+    result = data.get("result")
+
+    # 指令结构体不为None
+    if result is not None:
+        # 获取指令执行结果
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None or perform_action_result == "failure":
+            # 指令执行结果为:None 或 失败
+            return make_fail_result(device_id, task_id)
+        if perform_action_result == "stop":
+            # 指令执行结果为:终止
+            return make_stop_result(device_id, task_id)
+        else:
+            return None
+    else:
+        """
+        启动指令
+        启动app
+        """
+        return make_task_start_app(
+            data,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            timeout=timeout,
+            sleep_time=sleep_time
+        )
+
+
+"""
+启动APP任务
+:param data 客户端请求的参数字典
+:param target_app app标识
+:param target_version 目标APP的指定版本
+:param package_name 目标APP的id
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_start_app(
+        data,
+        target_app,
+        target_version,
+        package_name,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 生成新的任务id
+    action_id = make_new_task_id()
+    # 生成app启动json
+    action_dict = scene.oprator.atom_data.start_app(
+        request_id=action_id,
+        target_app=target_app,
+        target_version=target_version,
+        package_name=package_name,
+        timeout=timeout,
+        sleep_time=sleep_time
+    )
+    # 更新redis
+    update_redis_step(device_id, action_id, 1, 9)
+    # 记录日志
+    log_msg(device_id, "action0_id", action_id)
+    # 返回生成app启动json字典
+    log_msg_return(device_id, action_dict)
+    return action_dict
+
+
+"""
+关闭APP任务
+:param task_id 任务id
+:param data 客户端请求的参数字典
+:param target_app app标识
+:param target_version 目标APP的指定版本
+:param package_name 目标APP的id
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_stop_app(
+        task_id,
+        data,
+        step_index,
+        target_app,
+        target_version,
+        package_name,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 生成新的任务id
+    action_id = make_new_task_id()
+    # 生成app启动json
+    action_dict = scene.oprator.atom_data.stop_app(
+        request_id=action_id,
+        target_app=target_app,
+        target_version=target_version,
+        package_name=package_name,
+        timeout=timeout,
+        sleep_time=sleep_time
+    )
+    # 更新redis
+    update_redis_step(device_id, action_id, 1, step_index)
+    # 记录日志
+    log_msg(device_id, "action0_id", action_id)
+    # 返回生成app启动json字典
+    log_msg_return(device_id, action_dict)
+    end_task(device_id, task_id)
+    return action_dict
+
+
+"""
+点击原生控件任务
+需指定控件id,从weditor中抓取
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param target_version 目标APP的指定版本
+:param package_name 目标APP的id
+:param control_ids 
+:param control_id 控件的唯一id
+:param item_index 
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_click_widget(
+        data,
+        step_index,
+        target_app,
+        target_version,
+        package_name,
+        control_ids="",
+        control_id="",
+        item_index=0,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step_id = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step_id, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        action_dict = scene.oprator.atom_data.single_click_by_control(
+            action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            control_ids=control_ids,
+            control_id=control_id,
+            item_index=item_index,
+            timeout=timeout,
+            sleep_time=sleep_time
+        )
+        # 更新redis
+        update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+点击文本
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param package_name 目标APP的id
+:param target_version 目标APP的指定版本
+:param text 要点击的文本
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_click_text(
+        data,
+        step_index,
+        target_app,
+        package_name,
+        target_version,
+        text,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step_id = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step_id, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        # 生成点击图片字典
+        action_dict = scene.oprator.atom_data.single_click_by_text(
+            request_id=action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            text=text,
+            timeout=timeout,
+            sleep_time=sleep_time
+        )
+        # 更新redis
+        update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+滑动屏幕任务
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param package_name 目标APP的id
+:param target_version 目标APP的指定版本
+:param scale 滑动比例
+:param direction 滑动方向
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_swipe_screen(
+        data,
+        step_index,
+        target_app,
+        package_name,
+        target_version,
+        scale=0.5,
+        direction="up",
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        # 生成滑动屏幕操作json
+        action_dict = scene.oprator.atom_data.swipe_screen(
+            request_id=action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            scale=scale,
+            timeout=timeout,
+            sleep_time=sleep_time,
+            direction=direction
+        )
+        # 更新redis
+        update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+连续滑动屏幕任务
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param package_name 目标APP的id
+:param target_version 目标APP的指定版本
+:param scale 滑动比例
+:param direction 滑动方向
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_continual_swipe_screen(
+        data,
+        step_index,
+        target_app,
+        package_name,
+        target_version,
+        scale=0.5,
+        direction="up",
+        is_need_loop=False,
+        loop_count=1,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        # 生成滑动屏幕操作json
+        action_dict = scene.oprator.atom_data.swipe_screen(
+            request_id=action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            scale=scale,
+            timeout=timeout,
+            sleep_time=sleep_time,
+            direction=direction
+        )
+        if is_need_loop == False:
+            # 更新redis
+            update_redis_step(device_id, action_id, step_index + 1, step_index)
+        else:
+            loop_key = loop_key_format(device_id, str(step_index))
+            cur_loop_count = redis_client.get(loop_key)
+            if cur_loop_count == None:
+                cur_loop_count = 0
+            else:
+                cur_loop_count = int(cur_loop_count)
+            if cur_loop_count < loop_count:
+                update_redis_step(device_id, action_id, step_index, step_index)
+                cur_loop_count += 1
+                redis_client.set(loop_key, cur_loop_count)
+            else :
+                update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+点击图片任务,通过图片识别触发
+需指定图片的文件路径
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param package_name 目标APP的id
+:param target_version 目标APP的指定版本
+:param pic_base64 图片的base64字符串
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_click_image(
+        data,
+        step_index,
+        target_app,
+        package_name,
+        target_version,
+        pic_base64,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step_id = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step_id, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        # 生成点击图片字典
+        action_dict = scene.oprator.atom_data.click_pic(
+            request_id=action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            pic_base64=pic_base64,
+            timeout=timeout,
+            sleep_time=sleep_time
+        )
+        # 更新redis
+        update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+返回上一页
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param package_name 目标APP的id
+:param target_version 目标APP的指定版本
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_click_back(
+        data,
+        step_index,
+        target_app,
+        package_name,
+        target_version,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step_id = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step_id, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        # 生成点击图片字典
+        action_dict = scene.oprator.atom_data.back_last_page(
+            request_id=action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            timeout=timeout,
+            sleep_time=sleep_time
+        )
+        # 更新redis
+        update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+根据id输入文本内容
+:param data 客户端请求的参数字典
+:param step_index 当前指定到第几步
+:param target_app app标识
+:param package_name 目标APP的id
+:param target_version 目标APP的指定版本
+:param control_id 控件的id
+:param content 要输入的文本
+:param timeout 超时时间 s
+:param sleep_time 休眠时间 s
+"""
+
+
+def make_task_send_text(
+        data,
+        step_index,
+        target_app,
+        package_name,
+        target_version,
+        control_id,
+        content,
+        timeout=30,
+        sleep_time=10
+):
+    # 获取设备id
+    device_id = data.get("deviceID")
+    # 获取指令id
+    perform_action_id = data.get("performActionId")
+    # 获取 指令结构体
+    result = data.get("result")
+    # 获取任务执行状态
+    perform_action_result = result.get("performActionResult")
+    step_id = read_redis_step_id(device_id, perform_action_id, step_index)
+    # 获取redis缓存的操作id
+    last_action_id = read_redis_last_operate_id(device_id)
+
+    # 判断是否可以执行该步骤
+    if can_execute_step(step_id, last_action_id, perform_action_id, perform_action_result):
+        # 生成新的任务id
+        action_id = make_new_task_id()
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id_{load_log_mark(step_index)}", int(last_action_id))
+        # 生成点击图片字典
+        action_dict = scene.oprator.atom_data.send_text_by_control(
+            request_id=action_id,
+            target_app=target_app,
+            target_version=target_version,
+            package_name=package_name,
+            control_id=control_id,
+            content=content,
+            timeout=timeout,
+            sleep_time=sleep_time
+        )
+        # 更新redis
+        update_redis_step(device_id, action_id, step_index + 1, step_index)
+        # 记录日志
+        log_msg(device_id, f"action{step_index}_id", action_id)
+        # 返回新生成的操作json字典
+        log_msg_return(device_id, action_dict)
+        return action_dict
+    else:
+        return None
+
+
+"""
+生成日志标记
+"""
+
+
+def load_log_mark(step):
+    log_mark = "mem"
+    if step == 1:
+        log_mark = "start"
+    return log_mark
+
+
+"""
+生成新的任务id
+"""
+
+
+def make_new_task_id():
+    return int(round(time.time() * 1000))
+
+
+"""
+记录格式化日志
+"""
+
+
+def log_msg(device_id, msg, action_id):
+    loggerKit.info("设备:{0}, {1}:{2}", device_id, msg, action_id)
+
+
+"""
+记录返回内容
+"""
+
+
+def log_msg_return(device_id, result):
+    loggerKit.info("返回数据:{0}, {1}", device_id, result)
+
+
+"""
+判断是否可以执行某个步骤
+"""
+
+
+def can_execute_step(step_id, last_action_id, perform_action_id, perform_action_result):
+    if step_id is not None and \
+            int(step_id) == 1 and \
+            last_action_id is not None and \
+            int(perform_action_id) == int(last_action_id) and \
+            perform_action_result == "success":
+        return True
+    else:
+        return False
+
+
+"""
+根据图片名返回图片base64字符串
+"""
+
+
+def read_pic_base64_string(pic_path):
+    with open(pic_path, "rb") as image_file:
+        try:
+            encoded_string = "data:image/png;base64," + base64.b64encode(image_file.read()).decode('utf-8')
+        except Exception as e:
+            print(f"img_to_base64 error:{e}")
+    return encoded_string
+
+
+"""
+更新redis任务id状态
+"""
+
+
+def update_redis_step(device_id, action_id, set_step, del_setp):
+    """
+    每次操作完成后会将对应的操作唯一id存储到redis
+    并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+    """
+    redis_client.delete(step_key_format(f"{device_id}_{action_id}", del_setp))
+    redis_client.set(operate_key_format(device_id), action_id)
+    redis_client.set(step_key_format(f"{device_id}_{action_id}", set_step), "1")
+
+"""
+生成redis操作key
+"""
+
+
+def operate_key_format(key):
+    return f"{key}operate"
+
+
+"""
+生成redis步骤key
+"""
+
+
+def step_key_format(key, step):
+    return f"{key}_step{step}"
+
+
+"""
+生成redis循环key
+"""
+
+
+def loop_key_format(key, step):
+    return f"{key}_loop{step}"
+
+"""
+生成终止json返回数据
+"""
+
+
+def make_stop_result(device_id, task_id):
+    return make_result(device_id, task_id, -2, "指令被用户终止")
+
+
+"""
+生成失败json返回数据
+"""
+
+
+def make_fail_result(device_id, task_id):
+    return make_result(device_id, task_id, -2, "fail, performActionResult is null")
+
+
+"""
+生成json返回数据
+"""
+
+
+def make_result(device_id, task_id, code, message):
+    return_dict = {
+        "data": "",
+        "code": str(code),
+        "message": message
+    }
+
+    # del_key_vague(device_id)
+    callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+    return return_dict
+
+
+"""
+读取redis最后一次operate的id
+"""
+
+
+def read_redis_last_operate_id(device_id):
+    return redis_client.get(device_id + "operate")
+
+
+"""
+读取redis某一步的操作id
+"""
+
+
+def read_redis_step_id(device_id, action_id, step):
+    if action_id == "" or action_id == None:
+        return redis_client.get(f"{device_id}_step{step}")
+    else:
+        return redis_client.get(f"{device_id}_{action_id}_step{step}")
+
+
+"""
+结束任务,恢复任务状态
+"""
+
+
+def end_task(device_id, task_id):
+    del_key_vague(device_id)
+    # 回调任务中心修改任务状态
+    callback_task(None, None, task_id, device_id, 1, None)

+ 48 - 0
func/check_app.py

@@ -0,0 +1,48 @@
+import subprocess
+
+from tools import loggerKit
+
+
+# 检查app运行状态
+def is_app_running(device_id, package_name):
+    # 执行 adb shell pidof 命令检测应用程序是否运行
+    command = ['adb', '-s', device_id, 'shell', 'pidof', package_name]
+    result = subprocess.run(command, capture_output=True, text=True)
+
+    # 检查命令执行结果
+    if result.returncode == 0 and result.stdout.strip().isdigit():
+        return True
+    else:
+        return False
+
+
+# 检查app运行状态
+def check_app_running(device_id, package_name):
+    # 执行 adb shell pidof 命令检测应用程序是否运行
+    try:
+        command = ['adb', '-s', device_id, 'shell', 'pidof', package_name]
+        result = subprocess.check_output(command, stderr=subprocess.STDOUT, text=True)
+        return result.strip().isdigit()
+    except subprocess.CalledProcessError as e:
+        loggerKit.error("Failed to check if app {0} is running on device {1}, 异常信息:{2}", package_name, device_id,
+                        e.output)
+        return True
+
+
+# 检查app运行状态
+# check app运行状态
+def check_app_status(device_id, package_name):
+    try:
+        # Execute adb command to check if the app process is running
+        command = ['adb', '-s', device_id, 'shell', 'pidof', package_name]
+        result = subprocess.run(command, capture_output=True, text=True, check=True)
+        # Check the return code to determine if the app process is running
+        if result.returncode == 0:
+            return True
+        else:
+            loggerKit.error("app {0} 未运行 on device {1}", package_name, device_id)
+            return False
+    except subprocess.CalledProcessError as e:
+        loggerKit.error("Failed to check if app {0} is running on device {1}, 异常信息:{2}", package_name, device_id,
+                        e)
+        return False

+ 267 - 0
func/check_device.py

@@ -0,0 +1,267 @@
+import os
+import subprocess
+import sys
+import telnetlib
+import threading
+import time
+
+import uiautomator2
+import uiautomator2 as u2
+import yaml
+
+from task.task_dict import script_status
+from tools import loggerKit
+from tools.common_util import extract_latency, check_network_quality
+from tools.device_status import DeviceStatus
+
+# 读取 YAML 文件
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+
+wework_package = config['wework']['package']
+
+# net_domain = 'http://api-qa.risingauto.net/'
+net_domain = config['bmp-cp']['net_domain']
+
+# extern_domain = 'https://api-qa.risingauto.com/'
+extern_domain = config['bmp-cp']['extern_domain']
+
+
+# 获取连接设备
+def get_local_devices():
+    # 执行 adb 命令并获取输出
+    result = subprocess.run(['adb', 'devices'], capture_output=True, text=True)
+
+    # 解析输出,获取已连接设备列表
+    output_lines = result.stdout.strip().split('\n')
+    devices = []
+    for line in output_lines[1:]:
+        if '\tdevice' in line:
+            device_info = line.split('\t')[0]
+            devices.append(device_info)
+
+    # 输出已连接设备列表
+    if devices:
+        loggerKit.info("已连接设备:")
+        for device in devices:
+            loggerKit.info(device)
+    else:
+        loggerKit.info("没有已连接的设备")
+
+    return devices
+
+
+# 检验设备是否连接成功
+def check_device_connect(device):
+    devices = get_local_devices()
+    if device in devices:
+        loggerKit.info(device + "已连接")
+        return True
+    else:
+        loggerKit.info(device + "未连接")
+        return False
+
+
+# 判断网络代理端口是否是通的
+def check_telnet(host, port):
+    '''
+
+    :param host: host
+    :param port: port
+    :return:
+    '''
+    try:
+        # 创建Telnet对象
+        tn = telnetlib.Telnet(host, port, timeout=10)
+        tn.close()
+        return True
+    except ConnectionRefusedError:
+        return False
+    except TimeoutError:
+        return False
+    except Exception as e:
+        print("An error occurred:", e)
+        return False
+
+
+'''
+通用手机截图
+'''
+
+
+def cap_device(d, device, task_id):
+    '''
+
+    :param d:  uiautomator2 对象
+    :param device:  设备id
+    :param task_id:  任务id
+    :return:
+    '''
+
+    try:
+        app_dir = os.path.dirname(sys.argv[0])
+        screenshot_path = os.path.join(app_dir, f'screen/{device}/')
+        tmp_path = screenshot_path.replace(":", "_").replace(".", "_")
+
+        # 检查路径是否存在
+        if not os.path.exists(tmp_path):
+            # 创建多级目录
+            os.makedirs(os.path.dirname(tmp_path))
+
+        file_path = os.path.join(tmp_path, f'{device}_{task_id}_{time.time()}.png')
+        d.screenshot().save(file_path)
+
+        loggerKit.info(f'设备:{device} 执行任务出现异常,截图保存:{file_path}, tmp_path:{tmp_path}')
+
+    except Exception as e:
+
+        loggerKit.error(f'设备:{device} 执行任务出现异常后截图保存发生异常:{str(e)}')
+
+
+'''
+check 手机设备状态
+'''
+
+
+def check_single_adb_connect(device, mobile):
+    target_url = extern_domain.replace("https://", "").replace("http://", "").replace("/", "")
+    cmd_conn = f"adb connect {device}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    loggerKit.info(f"{conn_output}")
+
+    if b'Connection refused' in conn_output:
+        loggerKit.info('手机待激活{0}->{1},不能领取任务', device, mobile)
+        return DeviceStatus.Mobile_Phone_To_Be_Activated
+    elif b'already connected' in conn_output:
+
+        # 再次check 设备的 atx agent 服务状态
+        w = uiautomator2.connect(device)
+        # check atx agent 服务状态
+        if w.agent_alive:
+            loggerKit.info('atx-agent 服务正常, atx 已连接{0}->{1}, 再次成功连接', device, mobile)
+        else:
+            # 启动atx-agent服务,防止服务手动误关闭
+            start_atx_agent = f"adb -s {device} shell /data/local/tmp/atx-agent server -d --nouia "
+            opts_output = subprocess.check_output(start_atx_agent, shell=True)
+            loggerKit.info(f"启动设备{device}->{mobile} atx agent 服务, {opts_output}, 再次成功连接")
+
+        # check 设备是否在线
+        cmd_show = f"adb devices  -l | grep {device}"
+        show_output = subprocess.check_output(cmd_show, shell=True)
+        loggerKit.info(f"设备{device}->{mobile}, {show_output}")
+
+        if b"device product" in show_output:
+            loggerKit.info("设备在线{0}->{1}, 可以领取任务", device, mobile)
+
+            d = uiautomator2.connect(device)
+            loggerKit.info("{0}->{1}=>{2}", d.uiautomator.running(), device, mobile)
+            if d.uiautomator.running():
+                # loggerKit.info('atx 连接正常{0}->{1}, uiautomator2 服务正常, 开始判断设备网络质量', device, mobile)
+                loggerKit.info('atx 连接正常{0}, uiautomator2 服务正常', device)
+
+                # 判断手机端网络质量
+                cmd_ping = f"adb -s " + device + " shell ping -c 6 " + target_url
+                wifi_output = subprocess.check_output(cmd_ping, shell=True)
+
+                byte_array = bytearray(wifi_output)
+                byte_string = bytes(byte_array)
+
+                # Convert byte string to string
+                wifi_str = byte_string.decode('utf-8')
+
+                latencies = extract_latency(wifi_str, device=device, mobile=mobile)
+                network_quality = check_network_quality(latencies, device=device, mobile=mobile)
+
+                match network_quality:
+                    case DeviceStatus.Device_Net_Good:
+                        loggerKit.info('atx 连接正常{0}, uiautomator2 服务正常, 设备网络质量良好, 可以领取任务', device)
+                        return DeviceStatus.Available_For_Task
+                    case DeviceStatus.Device_Net_Common:
+                        loggerKit.info('atx 连接正常{0}, uiautomator2 服务正常, 设备网络质量一般, 可以领取任务', device)
+                        return DeviceStatus.Available_For_Task
+                    case DeviceStatus.Device_Net_Bad:
+                        loggerKit.info('atx 连接正常{0}, uiautomator2 服务正常, 设备网络质量较差, 不能领取任务', device)
+                        return DeviceStatus.Device_Await_Recover
+
+                if network_quality == DeviceStatus.Device_Net_Good:
+                    loggerKit.info('atx 连接正常{0}->{1}, uiautomator2 服务正常, 设备网络质量良好, 可以领取任务',
+                                    device, mobile)
+                    return DeviceStatus.Available_For_Task
+                elif network_quality == DeviceStatus.Device_Net_Common:
+                    loggerKit.info('atx 连接正常{0}->{1}, uiautomator2 服务正常, 设备网络质量一般, 可以领取任务',
+                                    device, mobile)
+                    return DeviceStatus.Available_For_Task
+                else:
+                    loggerKit.info('atx 连接正常{0}->{1}, uiautomator2 服务正常, 设备网络质量较差, 不能领取任务',
+                                    device, mobile)
+                    return DeviceStatus.Device_Await_Recover
+
+                return DeviceStatus.Available_For_Task
+
+            else:
+                loggerKit.info('atx 连接正常 {0}->{1}, uiautomator2 服务待人工启动, 不能领取任务', device, mobile)
+                time.sleep(1)
+
+                return DeviceStatus.To_Be_Manually_Started
+        elif b"offline product" in show_output:
+            loggerKit.info("设备不在线{0}->{1}, 不能领取任务", device, mobile)
+
+            return DeviceStatus.Device_Offline
+        else:
+            loggerKit.info("{0}->{1}=>{2}", device, mobile, show_output)
+
+            return DeviceStatus.Device_Offline
+    elif b'connected to' in conn_output:
+
+        u = uiautomator2.connect(device)
+        # check atx agent 服务状态
+        if u.agent_alive:
+            loggerKit.info('atx-agent 服务正常, atx 已连接{0}->{1}, 首次成功连接', device, mobile)
+        else:
+            # 启动atx-agent服务,防止服务手动误关闭
+            cmd_start_atx_agent = f"adb -s {device} shell /data/local/tmp/atx-agent server -d --nouia "
+            opt_output = subprocess.check_output(cmd_start_atx_agent, shell=True)
+            loggerKit.info(f"启动设备{device}->{mobile} atx agent 服务, {opt_output}, 首次成功连接")
+
+        return DeviceStatus.First_Success_Connect
+    else:
+        loggerKit('atx已连接{0}->{1}, 首次成功连接', device, mobile)
+        return DeviceStatus.First_Success_Connect
+
+
+def reboot_device(device_serial, exception):
+    # 连接设备
+    device = u2.connect(device_serial)
+
+    device.healthcheck()
+    device.screen_on()
+    device.unlock()
+    device.debug = False
+
+    # 重启企业微信
+    device.app_stop("com.tencent.wework")
+    device.app_start("com.tencent.wework")
+
+    loggerKit.info("异常, 重启设备 {0}, 异常原因:{1}", device_serial, exception)
+
+
+# 机器人操作重试机制
+def retry(operation, device_serial, content, retries=3, retry_interval=1):
+    for i in range(retries):
+        try:
+            return operation()
+        except Exception as e:
+            if i == retries - 1:  # 如果已经重试了retries次,那么抛出异常
+                raise e
+            time.sleep(retry_interval)  # 等待1秒再重试
+
+        loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 触发重试机制, 已经重试了{3}次",
+                       threading.current_thread().name, threading.get_ident(),
+                       device_serial, i)
+
+    status = script_status(0, 500, content, '')
+    reboot_device(device_serial, content)
+    loggerKit.error("thread[{0}=>{1}], 设备号:{2}, {3}", threading.current_thread().name,
+                    threading.get_ident(), device_serial, content)
+
+    return status

+ 20 - 0
gunicorn.conf

@@ -0,0 +1,20 @@
+# 是否开启debug模式
+debug = True
+# 访问地址
+bind = "0.0.0.0:80"
+# 工作进程数
+workers = 2
+# 工作线程数
+threads = 2
+# 超时时间
+timeout = 600
+# 输出日志级别
+loglevel = 'debug'
+# 存放日志路径
+pidfile = "logs/gunicorn.pid"
+# 存放日志路径
+accesslog = "logs/access.log"
+# 存放日志路径
+errorlog = "logs/debug.log"
+# gunicorn + apscheduler场景下,解决多worker运行定时任务重复执行的问题
+preload_app = True

+ 296 - 0
kuaishou/gifmaker_random_watch_video.py

@@ -0,0 +1,296 @@
+"""
+快手随机养号
+"""
+import random
+import time
+
+from func.action_func import del_key_vague
+from scene.oprator.atom_data import start_app, single_click_by_control, single_click_by_text, swipe_screen, \
+    many_click_by_position, click_pic, stop_app
+from task.task_job import callback_task
+from tools import redis_client, loggerKit
+from tools.pic_base64_util import pic_to_base64
+
+
+def gifmaker_random_watch_video(task_id, device_id, data):
+    loggerKit.info('请求信息:{0}'.format(data))
+
+    device_id = data.get("deviceID")
+    perform_action_id = data.get("performActionId")
+    result = data.get("result")
+    inner_data = data.get("data")
+
+    loggerKit.info("inner_data:{0}, keyword:{1}", inner_data, inner_data.get("resourceName"))
+
+    if result is not None:
+        """
+        非首个指令
+        """
+        perform_action_result = result.get("performActionResult")
+        if perform_action_result is None:
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            # 回调任务中心
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 指令执行失败
+        if perform_action_result == "failure":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is null"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令执行失败', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "stop":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "指令被用户终止"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '指令被用户终止', task_id, device_id, 0, None)
+            return return_dict
+
+        # 终止指令
+        if perform_action_result == "eleNotFound":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "未找到签到指示"
+            }
+
+            del_key_vague(device_id)
+            callback_task(500, '未找到签到指示,该账号当天可能已经签到过', task_id, device_id, 0, None)
+
+            return return_dict
+
+        # 元素未找到
+        if perform_action_result == "invalid operation":
+            # 回调任务中心
+            return_dict = {
+                "data": "",
+                "code": -2,
+                "message": "fail, performActionResult is not success"
+            }
+            del_key_vague(device_id)
+            callback_task(500, '任务执行失败,元素未找到', task_id, device_id, 0, None)
+
+            return return_dict
+
+        """
+        每次操作完成后会将对应的操作唯一id存储到redis
+        并且返回给手机端 手机端下次带着上个操作id来执行下一个操作
+        """
+        last_action_id = redis_client.get(device_id + "operate")
+        step0 = redis_client.get(f"{device_id}_step0")
+        if (step0 is not None and int(step0) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id)
+                and perform_action_result == "success"):
+            action1_id = int(round(time.time() * 1000))
+            """
+            发送第1条指令
+            点击:我(菜单)
+            """
+
+            action1_dict = single_click_by_control(action1_id, target_app="kuaishou",
+                                                   target_version="12.0.40.35075",
+                                                   package_name="com.smile.gifmaker",
+                                                   control_id="com.smile.gifmaker:id/tab_layout")
+
+            redis_client.set(device_id + "operate", action1_id)
+            redis_client.set(f"{device_id}_step1", "1")
+            redis_client.delete(f"{device_id}_step0")
+
+            loggerKit.info("taskId:{0}, action1_id:{1}", task_id, action1_id)
+
+            return action1_dict
+
+        step1 = redis_client.get(f"{device_id}_step1")
+        if step1 is not None and int(step1) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action2_id = int(round(time.time() * 1000))
+            """
+            发送第2条指令
+            点击:关注
+            """
+            action2_dict = single_click_by_control(action2_id,
+                                                   control_id="com.smile.gifmaker:id/following_tv",
+                                                   control_ids="com.smile.gifmaker:id/following_tv",
+                                                   target_app="kuaishou",
+                                                   target_version="12.0.40.35075",
+                                                   package_name="com.smile.gifmaker")
+
+            redis_client.set(device_id + "operate", action2_id)
+            redis_client.set(f"{device_id}_step2", "1")
+            redis_client.delete(f"{device_id}_step1")
+            loggerKit.info("taskId:{0}, action2_id:{1}", task_id, action2_id)
+
+            return action2_dict
+
+        step2 = redis_client.get(f"{device_id}_step2")
+        if step2 is not None and int(step2) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action3_id = int(round(time.time() * 1000))
+            """
+            发送第3条指令
+            点击:上汽荣威ROEWE
+            """
+            action3_dict = single_click_by_text(action3_id, text="上汽荣威ROEWE",
+                                                target_app="kuaishou",
+                                                target_version="12.0.40.35075",
+                                                package_name="com.smile.gifmaker")
+
+            redis_client.set(device_id + "operate", action3_id)
+            redis_client.set(f"{device_id}_step3", "1")
+            redis_client.delete(f"{device_id}_step2")
+            loggerKit.info("taskId:{0}, action3_id:{1}", task_id, action3_id)
+
+            return action3_dict
+
+        step3 = redis_client.get(f"{device_id}_step3")
+        if step3 is not None and int(step3) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action4_id = int(round(time.time() * 1000))
+            """
+            发送第4条指令
+            上滑随机屏
+            """
+            random_scale = random.randint(1, 3)
+            action4_dict = swipe_screen(action4_id, scale=random_scale,
+                                        target_app="kuaishou",
+                                        target_version="12.0.40.35075",
+                                        package_name="com.smile.gifmaker")
+            redis_client.set(device_id + "operate", action4_id)
+            redis_client.set(f"{device_id}_step4", "1")
+            redis_client.delete(f"{device_id}_step3")
+
+            return action4_dict
+
+        step4 = redis_client.get(f"{device_id}_step4")
+        if step4 is not None and int(step4) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action5_id = int(round(time.time() * 1000))
+            """
+            发送第5条指令
+            随机点击某个视频
+            """
+            random_index = random.randint(1, 5)
+            action5_dict = single_click_by_control(action5_id,
+                                                   control_id="com.smile.gifmaker:id/player_cover_container",
+                                                   control_ids="com.smile.gifmaker:id/player_cover_container",
+                                                   target_app="kuaishou",
+                                                   target_version="12.0.40.35075",
+                                                   package_name="com.smile.gifmaker",
+                                                   item_index=random_index)
+
+            redis_client.set(device_id + "operate", action5_id)
+            redis_client.set(f"{device_id}_step5", "1")
+            redis_client.delete(f"{device_id}_step4")
+
+            return action5_dict
+
+        # 视频点赞
+        step5 = redis_client.get(f"{device_id}_step5")
+        if step5 is not None and int(step5) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action6_id = int(round(time.time() * 1000))
+            """
+            发送第6条指令
+            视频点赞
+            """
+            action6_dict = many_click_by_position(action6_id,
+                                                  target_app="kuaishou",
+                                                  target_version="12.0.40.35075",
+                                                  package_name="com.smile.gifmaker")
+
+            redis_client.set(device_id + "operate", action6_id)
+            redis_client.set(f"{device_id}_step6", "1")
+            redis_client.delete(f"{device_id}_step5")
+
+            return action6_dict
+
+        # 视频收藏
+        step6 = redis_client.get(f"{device_id}_step6")
+        if step6 is not None and int(step6) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action7_id = int(round(time.time() * 1000))
+            """
+            发送第8条指令
+            视频收藏->判断视频是否已经收藏了
+            """
+            no_collected_base64 = pic_to_base64("kuaishou/screen/no_collected.png")
+            action7_dict = click_pic(action7_id, target_app="kuaishou",
+                                     target_version="12.0.40.35075",
+                                     package_name="com.smile.gifmaker",
+                                     pic_base64=no_collected_base64)
+
+            redis_client.set(device_id + "operate", action7_id)
+            redis_client.set(f"{device_id}_step7", "1")
+            redis_client.delete(f"{device_id}_step6")
+
+            loggerKit.info("设备:{0}, action7_id:{1}", device_id, action7_id)
+
+            return action7_dict
+
+        """
+         停止app
+         """
+        step7 = redis_client.get(f"{device_id}_step7")
+        if step7 is not None and int(step7) == 1 and last_action_id is not None and int(perform_action_id) == int(
+                last_action_id):
+            loggerKit.info("设备:{0}, last_action_id:{1}", device_id, int(last_action_id))
+            action8_id = int(round(time.time() * 1000))
+            """
+            停止指令
+            停止app
+            """
+
+            action8_dict = stop_app(action8_id, target_app="kuaishou",
+                                    target_version="12.0.40.35075",
+                                    package_name="com.smile.gifmaker")
+            del_key_vague(device_id)
+            loggerKit.info("设备:{0}, action8_id:{1}", device_id, action8_id)
+            callback_task(None, None, task_id, device_id, 1, None)
+
+            return action8_dict
+
+    else:
+
+        action0_id = int(round(time.time() * 1000))
+
+        """
+        启动指令
+        启动app
+        """
+
+        action0_dict = start_app(action0_id, target_app="xiaohongshu",
+                                 target_version="8.5.0",
+                                 package_name="com.xingin.xhs")
+
+        redis_client.set(device_id + "operate", action0_id)
+        redis_client.set(f"{device_id}_step0", "1")
+        redis_client.delete(f"{device_id}_step11")
+        loggerKit.info("设备:{0}, action0_id:{1}", device_id, action0_id)
+
+        return action0_dict

BIN
kuaishou/screen/no_collected.png


BIN
noval.png


BIN
ready_advise.png


+ 53 - 0
requirements.txt

@@ -0,0 +1,53 @@
+adbutils==2.2.1
+aniso8601==9.0.1
+anyio==4.2.0
+apkutils2==1.0.0
+blinker==1.7.0
+cached-property==1.5.2
+charset-normalizer==3.3.2
+cigam==0.0.3
+click==8.1.7
+decorator==5.1.1
+Deprecated==1.2.14
+deprecation==2.1.0
+filelock==3.13.1
+Flask==3.0.1
+Flask-RESTful==0.3.10
+h11==0.14.0
+httpcore==1.0.2
+httpx==0.26.0
+idna==3.6
+itsdangerous==2.1.2
+Jinja2==3.1.3
+logzero==1.7.0
+lxml==5.1.0
+MouseInfo==0.1.3
+packaging==23.2
+pillow==10.2.0
+progress==1.6
+py==1.11.0
+PyAutoGUI==0.9.54
+pyelftools==0.30
+PyGetWindow==0.0.9
+PyMsgBox==1.0.9
+pyperclip==1.8.2
+PyRect==0.2.0
+PyScreeze==0.1.30
+python3-xlib==0.15
+pytweening==1.0.7
+requests==2.31.0
+retry==0.9.2
+simplejson==3.19.2
+sniffio==1.3.0
+uiautomator2==2.16.25
+urllib3==2.1.0
+Werkzeug==3.0.1
+whichcraft==0.6.1
+wrapt==1.16.0
+xmltodict==0.13.0
+pyyaml==6.0
+redis==5.0.0
+apscheduler==3.10.4
+loguru==0.7.2
+bs4==0.0.2
+PySocks==1.7.1

+ 1 - 0
run.sh

@@ -0,0 +1 @@
+python app.py runserver 0.0.0.0:5000

+ 0 - 0
scene/__init__.py


BIN
scene/__pycache__/__init__.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/account_culture_job.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/account_interaction_job.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/account_interaction_user_job.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/cheyouquan_job.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/dongchedi_toutiao_response.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/interaction_poc.cpython-311.pyc


BIN
scene/dongchedi/__pycache__/user_comment_job.cpython-311.pyc


+ 311 - 0
scene/dongchedi/account_circle_poc.py

@@ -0,0 +1,311 @@
+# -*- coding: utf-8 -*-
+
+"""
+# description:懂车帝车友圈POC
+"""
+import time
+from collections import OrderedDict
+from datetime import datetime, timedelta
+from enum import Enum
+
+import uiautomator2
+from flask import jsonify
+
+from tools import loggerKit
+
+
+class account_circle_poc(Enum):
+    R7 = "R7"
+    F7 = "F7"
+
+    def name(self):
+        return "飞凡R7" if self == account_circle_poc.R7 else "飞凡F7"
+
+
+# 记录当前正在抓取的一级和二级tab
+current_primary_tab = None
+current_secondary_tab = None
+
+
+def crawl_data(device_id, car_type: account_circle_poc, task_transfer_time, sequence):
+    data = __get_circle_data(device_id, car_type.name(), task_transfer_time, sequence)
+    date = datetime.now().strftime('%y-%m-%d')
+    loggerKit.info("data:{0}, date:{1}", jsonify(data), str(date))
+
+
+def __get_circle_data(device_id, car_name, task_transfer_time, sequence):
+    """
+    抓取懂车帝车友圈数据
+    :param device_id: 执行任务的设备
+    :param car_name: 车友圈对应的汽车名称
+    :param task_transfer_time: 数据截止时间
+    """
+    d = uiautomator2.connect(device_id)
+    d.debug = False
+    d.screen_on()
+    d.unlock()
+    d.press("recent")
+    # 打开懂车帝app
+    d.app_stop("com.ss.android.auto")
+    d.app_start("com.ss.android.auto", ".policy.AutoPrivacyActivity")
+    time.sleep(5)
+    # app升级提醒弹窗处理
+    if d(resourceId="com.ss.android.auto:id/dtq").wait(1):
+        # 忽略升级
+        d(resourceId="com.ss.android.auto:id/dtq").click()
+    # 车友圈
+    d.xpath(
+        '//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[2]/android.widget.LinearLayout[1]').click()
+    time.sleep(2)
+    # 搜索飞凡R7兴趣圈
+    d(resourceId="com.ss.android.auto:id/ipm").click()
+    d(resourceId="com.ss.android.auto:id/gt8").set_text(car_name)
+    d(resourceId="com.ss.android.auto:id/fyw").click()
+    time.sleep(2)
+    d(text="车友圈").click()
+    d(text="进入车友圈").click()
+    #
+    tabs = [
+        # 动态
+        (d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/ce8"]/android.widget.LinearLayout[1]/android.widget.ImageView[1]'),
+         "动态", "dongtai"),
+        # 问答
+        (d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/ce8"]/android.widget.LinearLayout[2]/android.widget.ImageView[1]'),
+         "问答", "answer"),
+        # 口碑
+        (d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/ce8"]/android.widget.LinearLayout[3]/android.widget.ImageView[1]'),
+         "口碑", "opinions"),
+    ]
+    data = dict()
+    global current_primary_tab, current_secondary_tab
+    for item in tabs:
+        current_primary_tab = item[1]
+        current_secondary_tab = None
+        item[0].click()
+        time.sleep(3)
+        # 动态, 二级tab
+        if item[1] == "动态":
+            tmp_dict = dict()
+            for sub_tab in (("热门", "hot"), ("全部", "all"), ("精华", "essence")):
+                current_secondary_tab = sub_tab[0]
+                d(resourceId="com.ss.android.auto:id/jy8", text=sub_tab[0]).click()
+                time.sleep(3)
+                if sub_tab[0] == "全部":
+                    # 按发布时间排序列表
+                    d(resourceId="com.ss.android.auto:id/jy8", text="全部").click()
+                    d(resourceId="com.ss.android.auto:id/io3", text="按发布时间排序").click()
+                # 帖子列表
+                recycler_view = d(resourceId="com.ss.android.auto:id/hh0",
+                                  className="androidx.recyclerview.widget.RecyclerView")
+                time.sleep(2)
+                # 热门帖子固定抓取10条,根据任务排序上滑指定条数再抓取
+                tmp_dict[sub_tab[1]] = __parse_dongchedi_circle_list(recycler_view, d, task_transfer_time,
+                                                                     10 if sub_tab[0] == "热门" else 0, sequence)
+                time.sleep(1)
+            #
+            data[item[2]] = tmp_dict
+        else:
+            if item[1] == "问答":
+                # 按发布时间排序列表
+                d(resourceId="com.ss.android.auto:id/jy8", text="全部").click()
+                d(resourceId="com.ss.android.auto:id/io3", text="按发布时间排序").click()
+            # 帖子列表
+            recycler_view = d(resourceId="com.ss.android.auto:id/hh0",
+                              className="androidx.recyclerview.widget.RecyclerView")
+            time.sleep(2)
+            data[item[2]] = __parse_dongchedi_circle_list(recycler_view, d, task_transfer_time, 0, sequence)
+            time.sleep(1)
+        #
+        current_primary_tab = None
+        current_secondary_tab = None
+    #
+    d.app_stop("com.ss.android.auto")
+    return data
+
+
+def __parse_dongchedi_circle_list(listview, d: uiautomator2.Device, task_transfer_time, get_count, sequence):
+    if not listview.exists:
+        return []
+    #
+    transfer_date = datetime.strptime(task_transfer_time, '%Y-%m-%d').date()
+    ret = OrderedDict()
+    # get_count > 0 表示按固定数量抓取,按任务排序跳过前面的数据
+    if not isinstance(sequence, int):
+        sequence = 1
+    skip_count = (sequence - 1) * get_count
+    skip_cache = set()
+    #
+    while True:
+        if d.xpath('//*[@text="没有更多了"]').exists:
+            break
+        cell = listview.child(resourceId="com.ss.android.auto:id/dvd", className="android.widget.LinearLayout")
+        if not cell.exists:
+            break
+        # 帖子内容
+        while not d(resourceId="com.ss.android.auto:id/jdr").wait(timeout=1):
+            __retry(5, lambda: d.swipe_ext("up", 0.2))
+        #
+        content_view = d(resourceId="com.ss.android.auto:id/jdr")
+        content = content_view.get_text()[:20]
+        # 跳过指定数量的帖子
+        if get_count > 0 and skip_count > 0:
+            if content not in skip_cache:
+                skip_count -= 1
+                skip_cache.add(content)
+            #
+            __retry(5, lambda: d.swipe_ext("up", 0.2))
+            continue
+        #
+        if content in skip_cache:
+            __retry(5, lambda: d.swipe_ext("up", 0.2))
+            continue
+
+        # 根据帖子内容去重
+        cache_key = content
+        if cache_key in ret:
+            # 内容已抓取
+            __retry(5, lambda: d.swipe_ext("up", 0.2))
+            continue
+        # 发布时间
+        time_view = __find_view(d, "down", lambda: content_view.down(resourceId="com.ss.android.auto:id/ihu"))
+        if time_view is None:
+            __retry(5, lambda: d.swipe_ext("up", 0.2))
+            time.sleep(2)
+            continue
+        #
+        time_str = time_view.get_text().split("最后回复")[0]
+        time_str = __formate_time(time_str)
+        #
+        if get_count <= 0:
+            # 不是按固定数量抓取时才比对发帖时间
+            published_date = datetime.strptime(time_str, '%Y-%m-%d').date()
+            if published_date > transfer_date:
+                # 当前帖子时间晚于目标时间
+                if current_primary_tab == "问答":
+                    # 列表内容可以漏抓,提高列表滑动效率
+                    if published_date - transfer_date > timedelta(days=2):
+                        __retry(5, lambda: d.swipe_ext("up", 1))
+                    elif published_date - transfer_date > timedelta(days=1):
+                        __retry(5, lambda: d.swipe_ext("up", 0.5))
+                    else:
+                        __retry(5, lambda: d.swipe_ext("up", 0.2))
+                else:
+                    # 列表内容不能漏抓,不能滑动过快
+                    __retry(5, lambda: d.swipe_ext("up", 0.2))
+                #
+                continue
+            elif published_date < transfer_date:
+                # 当前帖子时间早于目标时间
+                break
+
+        # 昵称
+        nickname = __find_view(d, "up", lambda: content_view.up(resourceId="com.ss.android.auto:id/v")).get_text()
+        # 转发量
+        share_count = __intValue(
+            __find_view(d, "down", lambda: content_view.down(resourceId="com.ss.android.auto:id/jte")).get_text())
+        # 评论数
+        comment_count = __intValue(
+            __find_view(d, "down", lambda: content_view.down(resourceId="com.ss.android.auto:id/i9j")).get_text())
+        # 标题(标题有可能没有,所以这里不需要重试)
+        title = ""
+        title_view = content_view.up(resourceId="com.ss.android.auto:id/t", className="android.widget.TextView")
+        if title_view is not None and title_view.get_text() != "车主评分":
+            title = title_view.get_text()
+        #
+        item = {
+            'nickName': nickname,
+            'title': content if title == "" else title,
+            'publishDate': time_str,
+            'shareNum': share_count,
+            'commentNum': comment_count,
+        }
+        ret[cache_key] = item
+        print(f"nickname = {nickname}, share_count = {share_count}, comment_count = {comment_count}, "
+              f"content = {content}, title = {title}, time = {time_str}")
+        #
+        if 0 < get_count <= len(ret.values()):
+            # 按固定数量抓取,已达预期
+            break
+        # 列表滚动有时会出错,需要加重试
+        __retry(5, lambda: d.swipe_ext("up", 0.2))
+        time.sleep(1)
+    #
+    return list(ret.values())
+
+
+def __retry(times, operation):
+    sleep_time = 1
+    while times > 0:
+        try:
+            operation()
+            break
+        except Exception as e:
+            print("retry exception: \n")
+            print(e)
+            times -= 1
+            time.sleep(sleep_time)
+            sleep_time += 2
+
+
+def __intValue(value_str):
+    value = 0
+    try:
+        value = int(value_str)
+    except Exception as ex:
+        pass
+    return value
+
+
+def __find_view(d: uiautomator2.Device, direction, op):
+    try:
+        ret = op()
+        retry_count = 10
+        scroll_direction = ("up", "down")
+        while ret is None and retry_count > 0 and direction in scroll_direction:
+            retry_count -= 1
+            __retry(5, lambda: d.swipe_ext("up" if direction == "down" else "down", 0.1))
+            time.sleep(1)
+            ret = op()
+        #
+        return ret
+    except:
+        return None
+
+
+def __formate_time(time_str):
+    now = datetime.now()
+    if '刚刚' in time_str:
+        p_time = now
+    elif '分钟' in time_str:
+        minutes = int(time_str[:time_str.index('分')])
+        p_time = now - timedelta(minutes=minutes)
+    elif '小时' in time_str:
+        hours = int(time_str[:time_str.index('小')])
+        p_time = now - timedelta(hours=hours)
+    elif '昨天' in time_str:
+        p_time = now - timedelta(days=1)
+    elif '前天' in time_str:
+        p_time = now - timedelta(days=2)
+    elif '天' in time_str:
+        days = int(time_str[:time_str.index('天')])
+        p_time = now - timedelta(days=days)
+    elif '周' in time_str:
+        weeks = int(time_str[:time_str.index('周') - 1])
+        p_time = now - timedelta(weeks=weeks)
+    elif '年' not in time_str and '月' in time_str and '日' in time_str:
+        p_time = datetime.strptime(f'{now.year}年{time_str}', "%Y年%m月%d日").date()
+    else:
+        time_str = time_str.split()[0]
+        items = time_str.split("-")
+        if len(items) == 2:
+            p_time = datetime.strptime(f'{now.year}年{items[0]}月{items[1]}日', "%Y年%m月%d日").date()
+        elif len(items) == 3:
+            p_time = datetime.strptime(f'{items[0]}年{items[1]}月{items[2]}日', "%Y年%m月%d日").date()
+        else:
+            return None
+    #
+    p_time = p_time.strftime('%Y-%m-%d')
+    return p_time

+ 367 - 0
scene/dongchedi/account_culture_poc.py

@@ -0,0 +1,367 @@
+import random
+import subprocess
+import time
+
+import uiautomator2 as u2
+from uiautomator2 import Direction
+
+from tools import loggerKit
+from tools.common_util import cap_device
+
+
+class shell_status_content:
+    def __init__(self, execute_status, error_code, error_msg, result, content, nickname):
+        """
+
+        :rtype: object
+        """
+        # 0失败 1成功
+        self.execute_status = execute_status
+        self.error_code = error_code
+        self.error_msg = error_msg
+        self.result = result
+        self.content = content
+        self.nickname = nickname
+
+    def to_dict(self):
+        return {
+            'execute_status': self.execute_status,
+            'error_code': self.error_code,
+            'error_msg': self.error_msg,
+            'result': self.result,
+            'content': self.content,
+            'nickname': self.nickname
+        }
+
+
+# 保活任务 不打开app
+def keep_alive_sub_type(device):
+    cmd_conn = f"adb connect {device}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    print(f"{conn_output}")
+    d = u2.connect(device)
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    swipe_list = ['right', 'left']
+    d.swipe_ext(random.choice(swipe_list), 1)
+
+    status = shell_status_content(1, 0, '', '', '', '')
+    return status
+
+
+# 懂车帝 v7.9.0
+# 懂车帝demo
+def simulated_demo_operation(device, task_id):
+    cmd_conn = f"adb connect {device}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    print(f"{conn_output}")
+    d = u2.connect(device)
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    d.app_stop("com.ss.android.auto")
+    d.app_start('com.ss.android.auto', use_monkey=True)
+
+    time.sleep(2)
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/gxf"]').click_exists(timeout=0.25)
+    swipe_screen(d)
+    swipe_screen(d)
+    time.sleep(1)
+    # 如果弹出问答页面 处理问答页面
+    # handleQuestion(d)
+    # 模拟随机滑动几屏 随机浏览文章
+    # randomView(d)
+    # 搜索关键词
+    searchKeyword(d, task_id)
+
+    status = shell_status_content(1, 0, '', '', '', '')
+    return status
+
+
+# 懂车帝 v7.9.0
+# 3-5 天的养号操作
+def simulated_operation(device, title, task_id):
+    cmd_conn = f"adb connect {device}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    loggerKit.info(f"{conn_output}")
+    d = u2.connect(device)
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    try:
+        d.app_stop("com.ss.android.auto")
+        d.app_start('com.ss.android.auto', use_monkey=True)
+
+        # 跳过开屏广告
+        d.watcher.when("跳过广告").click()
+
+        # 跳过升级弹框
+        d.watcher.when("以后再说").click()
+
+        # 位置授权
+        d.watcher.when("本次运行允许").click()
+
+        # 稍后
+        d.watcher.when("稍后").click()
+
+        # 开始后台监控
+        # 默认监控间隔2.0s
+        d.watcher.start(2.0)
+
+        # 进入我的获取账号信息
+        d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[5]').click()
+        time.sleep(2)
+
+        account_name = ""
+        if d.xpath('//*[@resource-id="com.ss.android.auto:id/god"]').exists:
+            account_name = d.xpath('//*[@resource-id="com.ss.android.auto:id/god"]').get_text()
+
+        # 点击到首页
+        # d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[1]').click()
+        if d.xpath('//*[@text="首页"]').exists:
+            d.xpath('//*[@text="首页"]').click()
+        else:
+            d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[1]').click()
+
+        time.sleep(2)
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/gxf"]').click_exists(timeout=0.25)
+        swipe_screen(d)
+        swipe_screen(d)
+        time.sleep(1)
+        # 如果弹出问答页面 处理问答页面
+        handleQuestion(d)
+        # 模拟随机滑动几屏 随机浏览文章
+        randomView(d)
+
+        time.sleep(3)
+        # 搜索关键词
+        searchKeyword(d, title)
+
+        # 点击车友圈按钮
+        d.xpath('//*[@text="车友圈"]').click()
+        time.sleep(3)
+
+        # 进入车友圈
+        if d.xpath('//*[@text="进入车友圈"]').exists:
+            d.xpath('//*[@text="进入车友圈"]').click()
+        else:
+            d.xpath('//*[@text="进圈"]').click()
+        time.sleep(2)
+        # 点击加圈
+        circleTextView = d(resourceId="com.ss.android.auto:id/ief")
+        if circleTextView.exists:
+            text = circleTextView.get_text()
+            if text == "加圈":
+                circleTextView.click_exists(timeout=1)
+                time.sleep(0.5)
+                carFriendWindow = d(resourceId="com.ss.android.auto:id/ala")
+                if carFriendWindow.exists:
+                    d.xpath('//*[@resource-id="com.ss.android.auto:id/bpd"]').click_exists(timeout=1)
+        # 切换到精华tab
+        # d.xpath('//*[@text="精华"]').click_exists(timeout=2)
+        # time.sleep(0.25)
+        currentNum = 0
+        # 滑动几屏
+        while currentNum < random.randint(3, 5):
+            d.swipe_ext(Direction.FORWARD)
+            currentNum += 1
+            time.sleep(random.random())
+
+        # 随机滑动点击帖子
+        click = 0
+        while click <= random.randint(3, 5):
+            print("each = ", click)
+            readPost(d)
+            click += 1
+            d.swipe_ext(Direction.FORWARD)
+
+        # 切换到热门帖子
+        hotTabView = d.xpath('//*[@text="热门"]')
+        if hotTabView.exists:
+            hotTabView.click(timeout=1)
+            time.sleep(0.25)
+            # 随机滑动热门列表列表 、点击帖子
+            for each in range(1, 4):
+                readPost(d)
+                d.swipe_ext(Direction.FORWARD)
+
+        status = shell_status_content(1, 0, '', '', '', account_name)
+        d.app_stop("com.ss.android.auto")
+
+        return status
+
+    except Exception as e:
+        cap_device(d, device, task_id)
+
+        exception_status = shell_status_content(0, 500, str(e), '', '', '')
+        d.app_stop("com.ss.android.auto")
+
+        loggerKit.error(f"设备{device}, 任务{task_id}, dongchedi v7.9.0 自动化养号 异常: {str(e)}")
+
+        return exception_status
+
+
+# 搜索
+def searchKeyword(d, keyword):
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/c9c"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/c9c"]').click()
+    else:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/d34"]').click()
+
+    d.send_keys(keyword, clear=True)
+    time.sleep(1)
+    # 点击搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/g9e"]').click(timeout=5)
+    time.sleep(2)
+
+
+# 随机浏览
+def randomView(d):
+    recyclerView = d(resourceId="com.ss.android.auto:id/hgo", className="androidx.recyclerview.widget.RecyclerView")
+    if not recyclerView.exists:
+        d.swipe_ext("right", 1)
+
+        return
+    num = 0
+    # 循环随机浏览
+    while True:
+        if num >= 3:
+            break
+        num += 1
+        layoutG = recyclerView.child(resourceId="com.ss.android.auto:id/dv6", className="android.widget.LinearLayout")
+        if not layoutG.exists:
+            print("dv_ layout 不存在 则滑动")
+            swipe_screen(d)
+            continue
+        i = random.randint(0, len(layoutG) - 1)
+        layoutEle = layoutG[i]
+        # 点击对应的控件 可能是帖子 视频 图片等
+        layoutEle.click_exists(timeout=1)
+        # 进入详情之后之后
+        forwardNo = random.randint(1, 5)
+        # 随机向下滑动 forwardNo 次
+        j = 0
+        while j <= forwardNo:
+            imageView = d(resourceId="com.ss.android.auto:id/cz")
+            if imageView.exists:
+                print("点击的图片 不滑动 直接结束")
+                time.sleep(1)
+                break
+            d.swipe_ext(Direction.FORWARD)
+            j += 1
+            time.sleep(0.25)
+        # 随机睡眠 1 ~ 5秒
+        time.sleep(random.randint(1, 5))
+        # 回到推荐列表
+        d.swipe_ext("right", 1)
+        time.sleep(1)
+        swipe_screen(d)
+        time.sleep(0.5)
+
+
+# 处理问答
+def handleQuestion(d):
+    question = d(resourceId="athena-question-7130163961458065439")
+    if question.exists:
+        questionViewG = question.child(className="android.view.View")
+        if questionViewG.exists:
+            d.xpath('//*[@resource-id="athena-question-7130163961458065439"]/android.view.View[3]').click()
+            d.xpath('//*[@text="提交"]').click()
+
+
+# 进入文章进行处理
+def readPost(d):
+    # 判断右上角是否存在三个点
+    # 车友圈
+
+    recyclerView = d(resourceId="com.ss.android.auto:id/dv6")
+    if recyclerView.exists:
+        recyclerView.click()
+    else:
+        return
+
+    time.sleep(2)
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/iv_more"]').exists:
+        print('进入车友圈')
+
+        swipe = 0
+        while swipe <= random.randint(5, 8):
+            time.sleep(1)
+            d.swipe_ext("up", 1)
+            swipe += 1
+
+        d.swipe_ext("right", 1)
+
+
+    # 提问
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/kad"]').exists:
+        print('进入提问')
+
+        swipe = 0
+        while swipe <= random.randint(5, 8):
+            time.sleep(1)
+            d.swipe_ext("up", 1)
+            swipe += 1
+
+        d.swipe_ext("right", 1)
+
+
+
+    # 长文
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/ee1"]').exists:
+        print('进入长文')
+
+        swipe = 0
+        while swipe <= random.randint(5, 8):
+            time.sleep(1)
+            d.swipe_ext("up", 1)
+            swipe += 1
+
+        d.swipe_ext("right", 1)
+
+
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/c5n"]').exists:
+        # 短视频
+        print('进入短视频')
+
+        random_sleep = random.randint(10, 20)
+
+        time.sleep(random_sleep)
+
+        d.swipe_ext("right", 1)
+
+
+    else:
+        print('进入长视频')
+
+        random_sleep = random.randint(10, 20)
+
+        time.sleep(random_sleep)
+
+        d.swipe_ext("right", 1)
+
+
+# 随机滑动屏幕
+def swipe_screen(d):
+    # 获取设备的屏幕尺寸
+    screen_width = d.info["displayWidth"]
+    screen_height = d.info["displayHeight"]
+    print("screen_width 屏幕的宽度:", screen_width, "屏幕的长度:", screen_height)
+    # 设置滑动的起始点和终止点,可以根据需要进行调整
+    start_x = screen_width // 2  # 屏幕宽度的一半
+    start_y = random.randint(1000, screen_height)  # 起始点纵坐标,可以根据需要调整
+    end_x = screen_width // 2
+    end_y = random.randint(10, 300)
+    # 执行滑动操作
+    d.swipe(start_x, start_y, end_x, end_y)
+    # d.swipe_ext(Direction.FORWARD)
+    # 等待滑动完成,可以根据需要进行适当的等待
+    time.sleep(1)
+
+
+if __name__ == "__main__":
+    simulated_operation("QXNUT21905001550", "飞凡F7", 12121)

+ 115 - 0
scene/dongchedi/interaction_poc.py

@@ -0,0 +1,115 @@
+import datetime
+import random
+import time
+
+import requests
+import uiautomator2 as u2
+
+from tools.ip_util import get_local_ip, get_city
+
+
+def dong_action(mobile, device_serial, keyword, content):
+    # 连接设备
+    d = u2.connect(device_serial)
+
+    d.healthcheck()
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    # 关闭app
+    d.app_stop('com.ss.android.auto')
+
+    # 启动app
+    d.app_start('com.ss.android.auto', use_monkey=True)
+
+    txt = content.split('+')
+    print('len', len(txt))
+    print('type', type(txt))
+    time.sleep(3)
+
+    '''
+    搜索、点赞、收藏、回帖
+    '''
+
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/bug"]').click_exists()
+    d(focused=True).set_text(keyword)
+
+    time.sleep(2)
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/fd4"]').click_exists()
+
+    time.sleep(3)
+
+    d.xpath('//*[@text="视频"]').click_exists()
+
+    time.sleep(1)
+
+    while 1:
+        # 视频点击
+        for elem in d.xpath('//android.view.View').all():
+            d.click(0.23, 0.261)
+            time.sleep(2)
+            # 点赞
+            d.xpath('//*[@resource-id="com.ss.android.auto:id/dxu"]/android.widget.ImageView[1]').click_exists()
+            time.sleep(1)
+            # 收藏
+            d.xpath('//*[@resource-id="com.ss.android.auto:id/d1z"]').click_exists()
+            time.sleep(2)
+            # 回帖
+            k = d.xpath('//*[@resource-id="com.ss.android.auto:id/j_b"]')
+            j = d(text='说点什么...')
+            if k:
+                k.click_exists()
+            if j:
+                j.click_exists()
+            time.sleep(3)
+            # d.xpath('//*[contains(name(), "EditText")]').click_exists()
+            d(focused=True).set_text(random.choice(txt))
+            # d.send_keys(t)
+            # print('text:', t)
+            m = d.xpath('//*[@resource-id="com.ss.android.auto:id/f6f"]')
+            # n = d(text='发布')
+            if m:
+                d.xpath('//*[@resource-id="com.ss.android.auto:id/f6f"]').click_exists()
+            # if n:
+            #     d(text='发布').click()
+            # time.sleep(3)
+            # d.click(0.479, 0.23)
+            # 返回
+            d.xpath('//*[@resource-id="com.ss.android.auto:id/de"]').click_exists()
+            #
+            d.swipe_ext('up', scale=0.265)
+
+            # mobile, operate_type, ip, content, operate_time, result, channel, city
+
+            ip = get_local_ip()
+            if ip is not None:
+                city = get_city(ip)
+            else:
+                ip = ''
+                city = ''
+
+            # mobile = '13816996346'
+            # 记录回帖数据
+            url = 'http://localhost:8066/backend/operate'
+            params = {"channel": "dongchedi", "city": city, "ip": ip, "mobile": mobile, "operateTime": datetime.datetime.now(), "operateType": "2", "result": "操作成功"}
+
+            response = requests.post(url, data=params)
+            if response.ok:
+                result = response.json()
+                print(result)
+            else:
+                print('请求失败:', response.status_code)
+
+
+def dongcd_stop(device_serial):
+    # 连接设备
+    d = u2.connect(device_serial)
+
+    d.healthcheck()
+    d.screen_on()
+    d.unlock()
+    d.debug = True
+
+    # 关闭app
+    d.app_stop('com.ss.android.auto')

+ 572 - 0
scene/dongchedi/user_like_poc.py

@@ -0,0 +1,572 @@
+import json
+import time
+import uiautomator2 as u2
+from datetime import datetime, timedelta
+import subprocess
+
+from tools import loggerKit
+from difflib import SequenceMatcher
+
+
+class shell_status_content:
+    def __init__(self, execute_status, error_code, error_msg, result, content, nickname):
+        """
+
+        :rtype: object
+        """
+        # 0失败 1成功
+        self.execute_status = execute_status
+        self.error_code = error_code
+        self.error_msg = error_msg
+        self.result = result
+        self.content = content
+        self.nickname = nickname
+
+    def to_dict(self):
+        return {
+            'execute_status': self.execute_status,
+            'error_code': self.error_code,
+            'error_msg': self.error_msg,
+            'result': self.result,
+            'content': self.content,
+            'nickname': self.nickname
+        }
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }
+
+
+# 懂车帝 v7.8.3
+# 3-5 天的互动操作
+
+def simulated_operation(device_serial, keyword, task_id, media_channel, sub_resource_name):
+    cmd_conn = f"adb connect {device_serial}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    print(f"{conn_output}")
+    d = u2.connect(device_serial)
+    d.healthcheck()
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    # 跳过开屏广告
+    d.watcher.when("跳过广告").click()
+
+    # 跳过升级弹框
+    d.watcher.when("以后再说").click()
+
+    # 位置授权
+    d.watcher.when("本次运行允许").click()
+
+    # 稍后
+    d.watcher.when("稍后").click()
+
+    # 开始后台监控
+    # 默认监控间隔2.0s
+    d.watcher.start(2.0)
+
+    d.app_stop("com.ss.android.auto")
+    d.app_start('com.ss.android.auto', use_monkey=True)
+    loggerKit.info("懂车帝养号" + d.device_info['serial'] + ", 开始操作:{0}, {1},{2},{3}", device_serial, keyword,
+                    task_id, media_channel)
+
+    time.sleep(5)
+
+    # 进入我的获取账号信息
+    d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[5]').click()
+    time.sleep(2)
+
+    account_name = d.xpath('//*[@resource-id="com.ss.android.auto:id/goi"]').get_text()
+
+    # 点击到首页
+    if d.xpath('//*[@text="首页"]').exists:
+        d.xpath('//*[@text="首页"]').click()
+    else:
+        d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[1]').click()
+
+    # 点击开始搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/d0r"]').click(timeout=0.25)
+    time.sleep(2)
+
+    replace_word = keyword.replace('|', '')
+    # 输入框赋值
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').set_text(replace_word)
+
+    time.sleep(1)
+
+    # 点击搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/g61"]').click()
+
+    # 等待元素加载
+    time.sleep(5)
+
+    xpath = '//*[@text="{}"]'
+
+    # d.xpath(xpath.format(keyword)).all()[1].click()
+    if d.xpath(xpath.format(keyword)).all().__len__() > 0:
+        if d.xpath(xpath.format(keyword)).all().__len__() == 1:
+            d.xpath(xpath.format(keyword)).click()
+        else:
+            d.xpath(xpath.format(keyword)).all()[1].click()
+    else:
+        # 第一次存在元素加载问题
+        if d.xpath(xpath.format(keyword)).all().__len__() == 1:
+            d.xpath(xpath.format(keyword)).click()
+        elif d.xpath(xpath.format(keyword)).all().__len__() >= 1:
+            d.xpath(xpath.format(keyword)).all()[1].click()
+        else:
+            # 返回未找到帖子
+            if sub_resource_name is None or sub_resource_name == '':
+                status = shell_status_content(0, 501, '该帖子未获取到,且无对应发帖人,无法进行深层次搜索', '', '','')
+                d.app_stop("com.ss.android.auto")
+                return status
+            else:
+                return find_user(d, sub_resource_name, keyword, account_name, task_id)
+
+    # if not d.xpath('//*[@resource-id="com.ss.android.auto:id/hzw"]').exists and not d.xpath(
+    #         '//*[@resource-id="com.ss.android.auto:id/iv_more"]').exists and (not d.xpath(
+    #     '//*[@resource-id="com.ss.android.auto:id/ff6"]').exists or not d.xpath(
+    #     '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists):
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/hzw"]').exists and not d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/iv_more"]').exists and not d.xpath(
+        '//*[@resource-id="com.ss.android.auto:id/ff6"]').exists and not d.xpath(
+        '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        #  返回未找到帖子
+
+        if sub_resource_name is None or sub_resource_name == '':
+            status = shell_status_content(0, 501, '该帖子未获取到,且无对应发帖人,无法进行深层次搜索', '', '','')
+            d.app_stop("com.ss.android.auto")
+            return status
+        else:
+            return find_user(d, sub_resource_name, keyword, account_name, task_id)
+
+    return title_action(d, keyword, account_name, task_id)
+
+
+# 查询到用户主页寻找帖子
+def find_user(d, user_name, keyword, account_name, task_id):
+    time.sleep(3)
+
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').exists:
+        status = shell_status_content(0, 501, '该帖子类型不支持/搜索用户页面错误', '', '','')
+        d.app_stop("com.ss.android.auto")
+        return status
+    # 赋值搜索框 用户名称
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').set_text(user_name)
+
+    # 点击搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/g61"]').click()
+
+    time.sleep(3)
+
+    # time.sleep(2)
+    # d.xpath('//*[@text="小视频"]').click_exists()
+
+    if d.xpath('//*[@text="二手车"]').exists:
+
+        d(text='二手车').drag_to(text='综合', timeout=3)
+
+    else:
+
+        d(text='图片').drag_to(text='综合', timeout=3)
+
+    # time.sleep(1)
+    # d.xpath('//*[@text="车友圈"]').click_exists()
+    #
+    # time.sleep(1)
+    # d.xpath('//*[@text="问答"]').click_exists()
+
+    time.sleep(1)
+    d.xpath('//*[@text="用户"]').click()
+
+    time.sleep(3)
+
+    xpath = '//*[@text="{}"]'
+
+    user_name_list = d.xpath(xpath.format(user_name)).all()
+
+    # 根据用户名获取元素 搜索框一定有一个 所以<=1 则表示未搜索到用户
+    if user_name_list.__len__() <= 1:
+        # 当前用户没有获取到任何帖子
+        status = shell_status_content(0, 501, '未搜索到当前用户', '', '','')
+        return status
+
+    else:
+        # 懂车帝可能根据搜索算法 推出 是否仍要搜索目标选项, 这种情况对下标为2的进行点击。 正常情况对下标为1的进行点击
+        if not d.xpath('//*[@text="”的搜索结果。仍然搜索:"]').exists:
+            user_name_list[1].click()
+        elif user_name_list.__len__() >= 3:
+            user_name_list[2].click()
+
+    # if user_name_list.__len__() > 1:
+    #     user_name_list[user_name_list.__len__() - 1].click()
+    # else:
+    #     d.xpath(xpath.format(user_name)).click()
+
+    # 点击第一个用户进入首页
+    # d.xpath('//*[@resource-id="root"]/android.view.View[2]/android.view.View[1]/android.view.View[1]').click_exists()
+
+    time.sleep(2)
+
+    # 如果存在关注用户标签 进行关闭
+    check_follow_button(d)
+
+    time.sleep(1)
+
+    # 获取帖子发布时间
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/tv_time"]').exists:
+        # 当前用户没有获取到任何帖子
+        status = shell_status_content(0, 501, '当前用户没有获取到任何帖子', '', '','')
+
+        return status
+
+    # 预期时间
+    transfer_date = datetime.strptime('2023-10-01', '%Y-%m-%d').date()
+
+    while 1:
+        # 如果存在关注用户标签 进行关闭
+        check_follow_button(d)
+
+        publish_time = d.xpath('//*[@resource-id="com.ss.android.auto:id/tv_time"]').get_text()
+
+        publish_time = __formate_time(publish_time)
+
+        # 发布时间
+        publish_date = datetime.strptime(publish_time, '%Y-%m-%d').date()
+
+        # 获取到所有帖子标题进行模糊匹配
+        text_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').all()
+        for text in text_list:
+            matcher = SequenceMatcher(None, text.text, keyword)
+
+            similarity_ratio = matcher.ratio()
+
+            if similarity_ratio >= 0.8:
+                text.click()
+                loggerKit.info("任务{0},用户{1}下搜索到{2}对应帖子 进行点击并操作", task_id, account_name, keyword)
+                return title_action(d, keyword, account_name, task_id)
+
+        # 如果能够根据标题搜索到帖子
+        # if d.xpath(xpath.format(keyword)).exists:
+        #     d.xpath(xpath.format(keyword)).click()
+        #     return title_action(d, keyword, account_name)
+
+        # 先判断是否超过10月1号
+        if transfer_date > publish_date:
+            # 已经抓取到10月1号帖子 仍未获取到该帖子
+            print('已经获取到10月1号帖子')
+            status = shell_status_content(0, 501, '已经抓取到10月1号帖子 仍未获取到该帖子,不进行下一步获取', '', '','')
+
+            return status
+        elif d.xpath('//*[@text="没有更多了"]').exists:
+            # 没有更多帖子
+            print('没有更多了展示')
+            status = shell_status_content(0, 501, "用户拉取到'没有更多了'标识,仍未获取到帖子", '', '','')
+
+            return status
+        else:
+            d.swipe_ext("up", 1)
+
+
+def check_follow_button(d):
+    # 如果存在关注用户标签 进行关闭
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/byg"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/byg"]').click()
+
+
+def __formate_time(time_str):
+    now = datetime.now()
+    if '刚刚' in time_str:
+        p_time = now
+    elif '分钟' in time_str:
+        minutes = int(time_str[:time_str.index('分')])
+        p_time = now - timedelta(minutes=minutes)
+    elif '小时' in time_str:
+        hours = int(time_str[:time_str.index('小')])
+        p_time = now - timedelta(hours=hours)
+    elif '昨天' in time_str:
+        p_time = now - timedelta(days=1)
+    elif '前天' in time_str:
+        p_time = now - timedelta(days=2)
+    elif '天' in time_str:
+        days = int(time_str[:time_str.index('天')])
+        p_time = now - timedelta(days=days)
+    elif '周' in time_str:
+        weeks = int(time_str[:time_str.index('周') - 1])
+        p_time = now - timedelta(weeks=weeks)
+    elif '年' not in time_str and '月' in time_str and '日' in time_str:
+        p_time = datetime.strptime(f'{now.year}年{time_str}', "%Y年%m月%d日").date()
+    else:
+        time_str = time_str.split()[0]
+        items = time_str.split("-")
+        if len(items) == 2:
+            p_time = datetime.strptime(f'{now.year}年{items[0]}月{items[1]}日', "%Y年%m月%d日").date()
+        elif len(items) == 3:
+            p_time = datetime.strptime(f'{items[0]}年{items[1]}月{items[2]}日', "%Y年%m月%d日").date()
+        else:
+            return None
+    #
+    p_time = p_time.strftime('%Y-%m-%d')
+    return p_time
+
+
+# 进入帖子进行操作
+def title_action(d, keyword, account_name, task_id):
+    time.sleep(3)
+    # 目前只支持对帖子进行操作
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').exists and not d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        # 可能受网络/帖子内容影响 元素未加载出来 等待三秒再重试加载
+        time.sleep(3)
+        if not retry(lambda: d.xpath('//*[@resource-id="com.tencent.wework:id/ff6"]').exists, task_id):
+            # if not d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').exists:
+            # 获取评论内容,首先获取原文内容如果没有获取到,则获取标题
+            status = shell_status_content(0, 501, "该文章类型不支持操作", '', '','')
+            d.app_stop("com.ss.android.auto")
+            return status
+
+    if d.xpath('//*[@text="视频"]').exists:
+        #  返回未找到帖子
+        status = shell_status_content(0, 501, '该帖子为视频类型不支持评论', '', '','')
+        d.app_stop("com.ss.android.auto")
+
+        return status
+
+    # comment_request_text = ''
+    # if d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').exists:
+    #     comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').get_text()
+    #
+    # elif d.xpath('//*[@resource-id="com.ss.android.auto:id/izg"]').exists:
+    #     comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/izg"]').get_text()
+    #
+    # elif d.xpath('//*[@resource-id="com.ss.android.auto:id/p"]').exists:
+    #     comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/p"]').get_text()
+    # else:
+    #     d.swipe_ext("up", 0.3)
+    #
+    #     text_view_text = ''
+    #     view_view_text = ''
+    #
+    #     # 获取文章
+    #     article_list = d.xpath('//android.widget.TextView').all()
+    #     if article_list is None or article_list.__len__() == 0:
+    #         text_view_text = keyword
+    #     else:
+    #         # if article_list.__len__() > 3:
+    #         #     article_list = article_list[3:]
+    #         for article in article_list:
+    #             # if article.text.contains('说点什么'):
+    #             #     break
+    #             text_view_text += article.text
+    #
+    #     # 获取文章
+    #     article_list = d.xpath('//android.view.View').all()
+    #     if article_list is None or article_list.__len__() == 0:
+    #         view_view_text = keyword
+    #     else:
+    #         # if article_list.__len__() > 3:
+    #         #     article_list = article_list[3:]
+    #         for article in article_list:
+    #             # if article.text.contains('说点什么'):
+    #             #     break
+    #             view_view_text += article.text
+    #
+    #     if len(view_view_text) > len(text_view_text):
+    #         comment_request_text = view_view_text
+    #     else:
+    #         comment_request_text = text_view_text
+    #
+    # request_data = {
+    #     "templateId": "dcd_comment",
+    #     "content": comment_request_text
+    # }
+
+    # response = httpx.post('http://47.116.62.124:3000/comment', json=request_data, timeout=120)
+    #
+    # if not response.is_success:
+    #     #  调用AIGC获取评论失败
+    #     status = shell_status_content(0, 500, "调用AIGC获取评论失败", '', '','')
+    #     d.app_stop("com.ss.android.auto")
+    #     return status
+    #
+    # data = json.loads(response.text)
+    #
+    # reply_text = data.get('text')
+    #
+    # reply_text_json = json.loads(reply_text)
+    #
+    # reply = reply_text_json.get('comment')
+
+    # response = httpx.post('http://47.116.62.124:3000/comment-saic-pv', json=request_data, timeout=120)
+    #
+    # if not response.is_success:
+    #     #  调用AIGC获取评论失败
+    #     status = shell_status_content(0, 500, "调用AIGC获取评论失败", '', '', '')
+    #     d.app_stop("com.ss.android.auto")
+    #     return status
+    #
+    # response_body = json.loads(response.text)
+    #
+    # data = response_body.get('data')
+    #
+    # reply_text = data.get('answer')
+    #
+    # reply_text_json = json.loads(reply_text)
+    #
+    # reply = reply_text_json.get('comment')
+    #
+    # if '内容太少' in reply or '对不起' in reply or reply is None or reply == '':
+    #     # 返回未获取合适回复内容
+    #     status = shell_status_content(0, 500, "无效评论,请求为:" + comment_request_text + "回复为:" + reply, '', '','')
+    #     d.app_stop("com.ss.android.auto")
+    #     return status
+
+    # 点赞
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        like_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/d6l"]').all()
+        if like_list.__len__() == 1:
+            like_list[0].click()
+        else:
+            like_list[like_list.__len__() - 1].click()
+    else:
+        like_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').all()
+        if like_list.__len__() == 1:
+            like_list[0].click()
+        else:
+            like_list[like_list.__len__() - 1].click()
+
+    # time.sleep(2)
+    #
+    # # 点击对话框
+    # if d.xpath('//*[@resource-id="com.ss.android.auto:id/kl2"]').exists:
+    #     d.xpath('//*[@resource-id="com.ss.android.auto:id/kl2"]').click()
+    # elif d.xpath('//*[@resource-id="com.ss.android.auto:id/dib"]').exists:
+    #     d.xpath('//*[@resource-id="com.ss.android.auto:id/dib"]').click()
+    #     time.sleep(2)
+    #
+    #     d.xpath('//*[@resource-id="com.ss.android.auto:id/iit"]').click()
+    #
+    # elif d.xpath('//*[@resource-id="com.ss.android.auto:id/die"]').exists:
+    #     d.xpath('//*[@resource-id="com.ss.android.auto:id/die"]').click()
+    #
+    # else:
+    #     d.xpath('//*[@resource-id="com.ss.android.auto:id/dvq"]').click()
+    #
+    #
+    # time.sleep(2)
+    #
+    # # 评论框赋值
+    # d.xpath('//*[@resource-id="com.ss.android.auto:id/daj"]').set_text(reply)
+    #
+    # time.sleep(2)
+    #
+    # # 发布
+    # d.xpath('//*[@resource-id="com.ss.android.auto:id/fx_"]').click()
+    #
+    # time.sleep(2)
+    #
+    # if d.xpath('//*[@text="发布评论"]').exists:
+    #     d.xpath('//*[@text="发布评论"]').click()
+
+    # content_result = content_detail(keyword, comment_request_text, reply, account_name)
+
+    status = shell_status_content(1, 0, "", '', '', account_name)
+
+    return status
+
+
+# 机器人重复查找元素 针对网络不顺畅的情况 或手机性能问题
+def retry(operation, device_serial, retries=11, retry_interval=3):
+    for i in range(retries):
+
+        loggerKit.info("懂车帝互动获取多次获取元素" + device_serial + "开始操作")
+
+        if operation():
+            return True
+
+        if i == retries - 1:
+            return False
+
+        time.sleep(retry_interval)  # 等待2秒再重试
+
+    return False
+
+
+if __name__ == "__main__":
+    # search_test('QXNUT21905001550','新车  售18.99万元,定位纯电中大型轿车,飞凡F7都市版正式上市')
+    # while 1:
+    #
+    json_str = '''{
+    "data": [
+        {
+            "taskId": 1163119,
+            "mediaChannel": "dongchedi",
+            "taskType": "testPOC",
+            "taskSubType": "dongchediAppointInteraction",
+            "taskSequenceId": "",
+            "taskDesc": "懂车帝指定账号互动",
+            "actionType": "content",
+            "resourceName": "为何飞凡广州车展带来的两道“开胃菜”,让人越吃越“舒适”?",
+            "subResourceName": null,
+            "executeRobotAccount": "testRobotAccount",
+            "executeRobotName": "testRobotAccount",
+            "deviceId": "1",
+            "content": "",
+            "answerType": null,
+            "materialUri": null,
+            "spiderType": null,
+            "sequence": null,
+            "demoTask": false
+        },
+        {
+            "taskId": 1163119,
+            "mediaChannel": "dongchedi",
+            "taskType": "testPOC",
+            "taskSubType": "dongchediAppointInteraction",
+            "taskSequenceId": "",
+            "taskDesc": "懂车帝指定账号互动",
+            "actionType": "content",
+            "resourceName": "为何飞凡广州车展带来的两道“开胃菜”,让人越吃越“舒适”?",
+            "subResourceName": null,
+            "executeRobotAccount": "testRobotAccount",
+            "executeRobotName": "testRobotAccount",
+            "deviceId": "1",
+            "content": "",
+            "answerType": null,
+            "materialUri": null,
+            "spiderType": null,
+            "sequence": null,
+            "demoTask": false
+        }
+    ],
+    "code": "00000000",
+    "message": "success"
+}'''
+
+    data_list = json.loads(json_str).get('data')
+
+    for data in data_list:
+        print(data.get('taskId'))
+
+    status = simulated_operation("7HX5T19911019170", "感受飞凡F7最快的一次充电", 199, 'dongchedi', 'Lightman317')
+
+    print(json.dumps(status, default=lambda o: o.__dict__, indent=4, ensure_ascii=False))

+ 578 - 0
scene/dongchedi/user_reply_poc.py

@@ -0,0 +1,578 @@
+import json
+import time
+import uiautomator2 as u2
+
+import yaml
+import httpx
+from datetime import datetime, timedelta
+import subprocess
+
+from tools import loggerKit
+from difflib import SequenceMatcher
+
+# 读取 YAML 文件
+with open('config.yaml', 'r') as file:
+    config = yaml.load(file, Loader=yaml.FullLoader)
+extern_domain = config['bmp-cp']['extern_domain']
+
+post_ai_gc_url = config['bmp-cp']['post_ai_gc_url']
+
+
+class shell_status_content:
+    def __init__(self, execute_status, error_code, error_msg, result, content, nickname):
+        """
+
+        :rtype: object
+        """
+        # 0失败 1成功
+        self.execute_status = execute_status
+        self.error_code = error_code
+        self.error_msg = error_msg
+        self.result = result
+        self.content = content
+        self.nickname = nickname
+
+    def to_dict(self):
+        return {
+            'execute_status': self.execute_status,
+            'error_code': self.error_code,
+            'error_msg': self.error_msg,
+            'result': self.result,
+            'content': self.content,
+            'nickname': self.nickname
+        }
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }
+
+
+# 懂车帝 v7.8.3
+# 3-5 天的互动操作
+
+def simulated_operation(device_serial, keyword, task_id, media_channel, sub_resource_name):
+    cmd_conn = f"adb connect {device_serial}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    print(f"{conn_output}")
+    d = u2.connect(device_serial)
+    d.healthcheck()
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    # 跳过开屏广告
+    d.watcher.when("跳过广告").click()
+
+    # 跳过升级弹框
+    d.watcher.when("以后再说").click()
+
+    # 位置授权
+    d.watcher.when("本次运行允许").click()
+
+    # 稍后
+    d.watcher.when("稍后").click()
+
+    # 开始后台监控
+    # 默认监控间隔2.0s
+    d.watcher.start(2.0)
+
+    d.app_stop("com.ss.android.auto")
+    d.app_start('com.ss.android.auto', use_monkey=True)
+    loggerKit.info("懂车帝养号" + d.device_info['serial'] + ", 开始操作:{0}, {1},{2},{3}", device_serial, keyword,
+                   task_id, media_channel)
+
+    time.sleep(5)
+
+    # 进入我的获取账号信息
+    d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[5]').click()
+    time.sleep(2)
+
+    account_name = d.xpath('//*[@resource-id="com.ss.android.auto:id/goi"]').get_text()
+
+    # 点击到首页
+    d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[1]').click()
+
+    # 点击开始搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/d0r"]').click(timeout=0.25)
+    time.sleep(2)
+
+    replace_word = keyword.replace('|', '')
+    # 输入框赋值
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').set_text(replace_word)
+
+    time.sleep(1)
+
+    # 点击搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/g61"]').click()
+
+    # 等待元素加载
+    time.sleep(5)
+
+    xpath = '//*[@text="{}"]'
+
+    # d.xpath(xpath.format(keyword)).all()[1].click()
+    if d.xpath(xpath.format(keyword)).all().__len__() > 0:
+        if d.xpath(xpath.format(keyword)).all().__len__() == 1:
+            d.xpath(xpath.format(keyword)).click()
+        else:
+            d.xpath(xpath.format(keyword)).all()[1].click()
+    else:
+        # 第一次存在元素加载问题
+        if d.xpath(xpath.format(keyword)).all().__len__() == 1:
+            d.xpath(xpath.format(keyword)).click()
+        elif d.xpath(xpath.format(keyword)).all().__len__() >= 1:
+            d.xpath(xpath.format(keyword)).all()[1].click()
+        else:
+            # 返回未找到帖子
+            if sub_resource_name is None or sub_resource_name == '':
+                status = shell_status_content(0, 501, '该帖子未获取到,且无对应发帖人,无法进行深层次搜索', '', '', '')
+                d.app_stop("com.ss.android.auto")
+                return status
+            else:
+                return find_user(d, sub_resource_name, keyword, account_name, task_id)
+
+    # if not d.xpath('//*[@resource-id="com.ss.android.auto:id/hzw"]').exists and not d.xpath(
+    #         '//*[@resource-id="com.ss.android.auto:id/iv_more"]').exists and (not d.xpath(
+    #     '//*[@resource-id="com.ss.android.auto:id/ff6"]').exists or not d.xpath(
+    #     '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists):
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/hzw"]').exists and not d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/iv_more"]').exists and not d.xpath(
+        '//*[@resource-id="com.ss.android.auto:id/ff6"]').exists and not d.xpath(
+        '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        #  返回未找到帖子
+
+        if sub_resource_name is None or sub_resource_name == '':
+            status = shell_status_content(0, 501, '该帖子未获取到,且无对应发帖人,无法进行深层次搜索', '', '', '')
+            d.app_stop("com.ss.android.auto")
+            return status
+        else:
+            return find_user(d, sub_resource_name, keyword, account_name, task_id)
+
+    return title_action(d, keyword, account_name, task_id)
+
+
+# 查询到用户主页寻找帖子
+def find_user(d, user_name, keyword, account_name, task_id):
+    time.sleep(3)
+
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').exists:
+        status = shell_status_content(0, 501, '该帖子类型不支持/搜索用户页面错误', '', '', '')
+        d.app_stop("com.ss.android.auto")
+        return status
+    # 赋值搜索框 用户名称
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').set_text(user_name)
+
+    # 点击搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/g61"]').click()
+
+    time.sleep(3)
+
+    # time.sleep(2)
+    # d.xpath('//*[@text="小视频"]').click_exists()
+
+    if d.xpath('//*[@text="二手车"]').exists:
+
+        d(text='二手车').drag_to(text='综合', timeout=3)
+
+    else:
+
+        d(text='图片').drag_to(text='综合', timeout=3)
+
+    # time.sleep(1)
+    # d.xpath('//*[@text="车友圈"]').click_exists()
+    #
+    # time.sleep(1)
+    # d.xpath('//*[@text="问答"]').click_exists()
+
+    time.sleep(1)
+    d.xpath('//*[@text="用户"]').click()
+
+    time.sleep(3)
+
+    xpath = '//*[@text="{}"]'
+
+    user_name_list = d.xpath(xpath.format(user_name)).all()
+
+    # 根据用户名获取元素 搜索框一定有一个 所以<=1 则表示未搜索到用户
+    if user_name_list.__len__() <= 1:
+        # 当前用户没有获取到任何帖子
+        status = shell_status_content(0, 501, '未搜索到当前用户', '', '', '')
+        return status
+
+    else:
+        # 懂车帝可能根据搜索算法 推出 是否仍要搜索目标选项, 这种情况对下标为2的进行点击。 正常情况对下标为1的进行点击
+        if not d.xpath('//*[@text="”的搜索结果。仍然搜索:"]').exists:
+            user_name_list[1].click()
+        elif user_name_list.__len__() >= 3:
+            user_name_list[2].click()
+
+    # if user_name_list.__len__() > 1:
+    #     user_name_list[user_name_list.__len__() - 1].click()
+    # else:
+    #     d.xpath(xpath.format(user_name)).click()
+
+    # 点击第一个用户进入首页
+    # d.xpath('//*[@resource-id="root"]/android.view.View[2]/android.view.View[1]/android.view.View[1]').click_exists()
+
+    time.sleep(2)
+
+    # 如果存在关注用户标签 进行关闭
+    check_follow_button(d)
+
+    time.sleep(1)
+
+    # 获取帖子发布时间
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/tv_time"]').exists:
+        # 当前用户没有获取到任何帖子
+        status = shell_status_content(0, 501, '当前用户没有获取到任何帖子', '', '', '')
+
+        return status
+
+    # 预期时间
+    transfer_date = datetime.strptime('2023-10-01', '%Y-%m-%d').date()
+
+    while 1:
+        # 如果存在关注用户标签 进行关闭
+        check_follow_button(d)
+
+        publish_time = d.xpath('//*[@resource-id="com.ss.android.auto:id/tv_time"]').get_text()
+
+        publish_time = __formate_time(publish_time)
+
+        # 发布时间
+        publish_date = datetime.strptime(publish_time, '%Y-%m-%d').date()
+
+        # 获取到所有帖子标题进行模糊匹配
+        text_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').all()
+        for text in text_list:
+            matcher = SequenceMatcher(None, text.text, keyword)
+
+            similarity_ratio = matcher.ratio()
+
+            if similarity_ratio >= 0.8:
+                text.click()
+                loggerKit.info("任务{0},用户{1}下搜索到{2}对应帖子 进行点击并操作", task_id, account_name, keyword)
+                return title_action(d, keyword, account_name, task_id)
+
+        # 如果能够根据标题搜索到帖子
+        # if d.xpath(xpath.format(keyword)).exists:
+        #     d.xpath(xpath.format(keyword)).click()
+        #     return title_action(d, keyword, account_name)
+
+        # 先判断是否超过10月1号
+        if transfer_date > publish_date:
+            # 已经抓取到10月1号帖子 仍未获取到该帖子
+            print('已经获取到10月1号帖子')
+            status = shell_status_content(0, 501, '已经抓取到10月1号帖子 仍未获取到该帖子,不进行下一步获取', '', '', '')
+
+            return status
+        elif d.xpath('//*[@text="没有更多了"]').exists:
+            # 没有更多帖子
+            print('没有更多了展示')
+            status = shell_status_content(0, 501, "用户拉取到'没有更多了'标识,仍未获取到帖子", '', '', '')
+
+            return status
+        else:
+            d.swipe_ext("up", 1)
+
+
+def check_follow_button(d):
+    # 如果存在关注用户标签 进行关闭
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/byg"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/byg"]').click()
+
+
+def __formate_time(time_str):
+    now = datetime.now()
+    if '刚刚' in time_str:
+        p_time = now
+    elif '分钟' in time_str:
+        minutes = int(time_str[:time_str.index('分')])
+        p_time = now - timedelta(minutes=minutes)
+    elif '小时' in time_str:
+        hours = int(time_str[:time_str.index('小')])
+        p_time = now - timedelta(hours=hours)
+    elif '昨天' in time_str:
+        p_time = now - timedelta(days=1)
+    elif '前天' in time_str:
+        p_time = now - timedelta(days=2)
+    elif '天' in time_str:
+        days = int(time_str[:time_str.index('天')])
+        p_time = now - timedelta(days=days)
+    elif '周' in time_str:
+        weeks = int(time_str[:time_str.index('周') - 1])
+        p_time = now - timedelta(weeks=weeks)
+    elif '年' not in time_str and '月' in time_str and '日' in time_str:
+        p_time = datetime.strptime(f'{now.year}年{time_str}', "%Y年%m月%d日").date()
+    else:
+        time_str = time_str.split()[0]
+        items = time_str.split("-")
+        if len(items) == 2:
+            p_time = datetime.strptime(f'{now.year}年{items[0]}月{items[1]}日', "%Y年%m月%d日").date()
+        elif len(items) == 3:
+            p_time = datetime.strptime(f'{items[0]}年{items[1]}月{items[2]}日', "%Y年%m月%d日").date()
+        else:
+            return None
+    #
+    p_time = p_time.strftime('%Y-%m-%d')
+    return p_time
+
+
+# 进入帖子进行操作
+def title_action(d, keyword, account_name, task_id):
+    time.sleep(3)
+    # 目前只支持对帖子进行操作
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').exists and not d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        # 可能受网络/帖子内容影响 元素未加载出来 等待三秒再重试加载
+        time.sleep(3)
+        if not retry(lambda: d.xpath('//*[@resource-id="com.tencent.wework:id/ff6"]').exists, task_id):
+            # if not d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').exists:
+            # 获取评论内容,首先获取原文内容如果没有获取到,则获取标题
+            status = shell_status_content(0, 501, "该文章类型不支持操作", '', '', '')
+            d.app_stop("com.ss.android.auto")
+            return status
+
+    if d.xpath('//*[@text="视频"]').exists:
+        #  返回未找到帖子
+        status = shell_status_content(0, 501, '该帖子为视频类型不支持评论', '', '', '')
+        d.app_stop("com.ss.android.auto")
+
+        return status
+
+    comment_request_text = ''
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').exists:
+        comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').get_text()
+
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/izg"]').exists:
+        comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/izg"]').get_text()
+
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/p"]').exists:
+        comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/p"]').get_text()
+    else:
+        d.swipe_ext("up", 0.3)
+
+        text_view_text = ''
+        view_view_text = ''
+
+        # 获取文章
+        article_list = d.xpath('//android.widget.TextView').all()
+        if article_list is None or article_list.__len__() == 0:
+            text_view_text = keyword
+        else:
+            # if article_list.__len__() > 3:
+            #     article_list = article_list[3:]
+            for article in article_list:
+                # if article.text.contains('说点什么'):
+                #     break
+                text_view_text += article.text
+
+        # 获取文章
+        article_list = d.xpath('//android.view.View').all()
+        if article_list is None or article_list.__len__() == 0:
+            view_view_text = keyword
+        else:
+            # if article_list.__len__() > 3:
+            #     article_list = article_list[3:]
+            for article in article_list:
+                # if article.text.contains('说点什么'):
+                #     break
+                view_view_text += article.text
+
+        if len(view_view_text) > len(text_view_text):
+            comment_request_text = view_view_text
+        else:
+            comment_request_text = text_view_text
+
+    request_data = {
+        "templateId": "dcd_comment",
+        "content": comment_request_text
+    }
+
+    # response = httpx.post('http://47.116.62.124:3000/comment', json=request_data, timeout=120)
+    #
+    # if not response.is_success:
+    #     #  调用AIGC获取评论失败
+    #     status = shell_status_content(0, 500, "调用AIGC获取评论失败", '', '','')
+    #     d.app_stop("com.ss.android.auto")
+    #     return status
+    #
+    # data = json.loads(response.text)
+    #
+    # reply_text = data.get('text')
+    #
+    # reply_text_json = json.loads(reply_text)
+    #
+    # reply = reply_text_json.get('comment')
+
+    response = httpx.post(post_ai_gc_url, json=request_data, timeout=120)
+
+    if not response.is_success:
+        #  调用AIGC获取评论失败
+        status = shell_status_content(0, 500, "调用AIGC获取评论失败", '', '', '')
+        d.app_stop("com.ss.android.auto")
+        return status
+
+    response_body = json.loads(response.text)
+
+    data = response_body.get('data')
+
+    reply_text = data.get('answer')
+
+    reply_text_json = json.loads(reply_text)
+
+    reply = reply_text_json.get('comment')
+
+    if '内容太少' in reply or '对不起' in reply or reply is None or reply == '':
+        # 返回未获取合适回复内容
+        status = shell_status_content(0, 500, "无效评论,请求为:" + comment_request_text + "回复为:" + reply, '', '', '')
+        d.app_stop("com.ss.android.auto")
+        return status
+
+    # 点赞
+    # if d.xpath('//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+    #     like_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/d6l"]').all()
+    #     if like_list.__len__() == 1:
+    #         like_list[0].click()
+    #     else:
+    #         like_list[like_list.__len__() - 1].click()
+    # else:
+    #     like_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').all()
+    #     if like_list.__len__() == 1:
+    #         like_list[0].click()
+    #     else:
+    #         like_list[like_list.__len__() - 1].click()
+    #
+    # time.sleep(2)
+
+    # 点击对话框
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/kl2"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/kl2"]').click()
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/dib"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/dib"]').click()
+        time.sleep(2)
+
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/iit"]').click()
+
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/die"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/die"]').click()
+
+    else:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/dvq"]').click()
+
+    time.sleep(2)
+
+    # 评论框赋值
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/daj"]').set_text(reply)
+
+    time.sleep(2)
+
+    # 发布
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/fx_"]').click()
+
+    time.sleep(2)
+
+    if d.xpath('//*[@text="发布评论"]').exists:
+        d.xpath('//*[@text="发布评论"]').click()
+
+    content_result = content_detail(keyword, comment_request_text, reply, account_name)
+
+    status = shell_status_content(1, 0, "", '', json.dumps(content_result.to_dict(), ensure_ascii=False), account_name)
+
+    return status
+
+
+# 机器人重复查找元素 针对网络不顺畅的情况 或手机性能问题
+def retry(operation, device_serial, retries=11, retry_interval=3):
+    for i in range(retries):
+
+        loggerKit.info("懂车帝互动获取多次获取元素" + device_serial + "开始操作")
+
+        if operation():
+            return True
+
+        if i == retries - 1:
+            return False
+
+        time.sleep(retry_interval)  # 等待2秒再重试
+
+    return False
+
+
+if __name__ == "__main__":
+    # search_test('QXNUT21905001550','新车  售18.99万元,定位纯电中大型轿车,飞凡F7都市版正式上市')
+    # while 1:
+    #
+    json_str = '''{
+    "data": [
+        {
+            "taskId": 1163119,
+            "mediaChannel": "dongchedi",
+            "taskType": "testPOC",
+            "taskSubType": "dongchediAppointInteraction",
+            "taskSequenceId": "",
+            "taskDesc": "懂车帝指定账号互动",
+            "actionType": "content",
+            "resourceName": "为何飞凡广州车展带来的两道“开胃菜”,让人越吃越“舒适”?",
+            "subResourceName": null,
+            "executeRobotAccount": "testRobotAccount",
+            "executeRobotName": "testRobotAccount",
+            "deviceId": "1",
+            "content": "",
+            "answerType": null,
+            "materialUri": null,
+            "spiderType": null,
+            "sequence": null,
+            "demoTask": false
+        },
+        {
+            "taskId": 1163119,
+            "mediaChannel": "dongchedi",
+            "taskType": "testPOC",
+            "taskSubType": "dongchediAppointInteraction",
+            "taskSequenceId": "",
+            "taskDesc": "懂车帝指定账号互动",
+            "actionType": "content",
+            "resourceName": "为何飞凡广州车展带来的两道“开胃菜”,让人越吃越“舒适”?",
+            "subResourceName": null,
+            "executeRobotAccount": "testRobotAccount",
+            "executeRobotName": "testRobotAccount",
+            "deviceId": "1",
+            "content": "",
+            "answerType": null,
+            "materialUri": null,
+            "spiderType": null,
+            "sequence": null,
+            "demoTask": false
+        }
+    ],
+    "code": "00000000",
+    "message": "success"
+}'''
+
+    data_list = json.loads(json_str).get('data')
+
+    for data in data_list:
+        print(data.get('taskId'))
+
+    status = simulated_operation("7HX5T19911019170", "感受飞凡F7最快的一次充电", 199, 'dongchedi', 'Lightman317')
+
+    print(json.dumps(status, default=lambda o: o.__dict__, indent=4, ensure_ascii=False))

+ 482 - 0
scene/dongchedi/user_sign_poc.py

@@ -0,0 +1,482 @@
+import json
+import time
+import uiautomator2 as u2
+import httpx
+from datetime import datetime, timedelta
+import subprocess
+
+from tools import loggerKit
+from difflib import SequenceMatcher
+
+
+class shell_status_content:
+    def __init__(self, execute_status, error_code, error_msg, result, content):
+        """
+
+        :rtype: object
+        """
+        # 0失败 1成功
+        self.execute_status = execute_status
+        self.error_code = error_code
+        self.error_msg = error_msg
+        self.result = result
+        self.content = content
+
+    def to_dict(self):
+        return {
+            'execute_status': self.execute_status,
+            'error_code': self.error_code,
+            'error_msg': self.error_msg,
+            'result': self.result,
+            'content': self.content
+        }
+
+
+# 回复详情
+class content_detail:
+    def __init__(self, keyword, title, reply, account_name):
+        """
+
+        :rtype: object
+        """
+        # 搜索关键词
+        self.keyword = keyword
+        self.title = title
+        self.reply = reply
+        self.account_name = account_name
+
+    def to_dict(self):
+        return {
+            'keyword': self.keyword,
+            'title': self.title,
+            'reply': self.reply,
+            'accountName': self.account_name
+        }
+
+
+# 懂车帝 v7.8.3
+# 签到操作
+
+def simulated_operation(device_serial, keyword, task_id, media_channel):
+    cmd_conn = f"adb connect {device_serial}"
+    conn_output = subprocess.check_output(cmd_conn, shell=True)
+    print(f"{conn_output}")
+    d = u2.connect(device_serial)
+    d.healthcheck()
+    d.screen_on()
+    d.unlock()
+    d.debug = False
+
+    # 跳过开屏广告
+    d.watcher.when("以后再说").click()
+
+    # 跳过升级弹框
+    d.watcher.when("以后再说").click()
+
+    # 开始后台监控
+    # 默认监控间隔2.0s
+    d.watcher.start(1.0)
+
+    d.app_stop("com.ss.android.auto")
+    d.app_start('com.ss.android.auto', use_monkey=True)
+    loggerKit.info("懂车帝养号" + d.device_info['serial'] + ", 开始操作:{0}, {1},{2}", device_serial,
+                    task_id, media_channel)
+
+    time.sleep(5)
+
+    # 进入我的获取账号信息
+    d.xpath('//*[@resource-id="android:id/tabs"]/android.widget.RelativeLayout[5]').click()
+    time.sleep(2)
+
+    account_name = d.xpath('//*[@resource-id="com.ss.android.auto:id/goi"]').get_text()
+
+    target_text = '签到'
+
+    xpath = f'//*[contains(@text, "{target_text}")]'
+
+    if d.xpath(xpath).exists:
+        print("找到了")
+        d.xpath(xpath).click()
+
+    if d.xpath('//*[@text="签到"]').exists:
+        d.xpath('//*[@text="签到"]').click()
+
+    status = shell_status_content(1, 0, '', '', '')
+    return status
+
+
+# 查询到用户主页寻找帖子
+def find_user(d, user_name, keyword, account_name, task_id):
+    time.sleep(3)
+
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').exists:
+        status = shell_status_content(0, 501, '该帖子类型不支持/搜索用户页面错误', '', '')
+        d.app_stop("com.ss.android.auto")
+        return status
+    # 赋值搜索框 用户名称
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/h1k"]').set_text(user_name)
+
+    # 点击搜索
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/g61"]').click()
+
+    time.sleep(3)
+
+    # time.sleep(2)
+    # d.xpath('//*[@text="小视频"]').click_exists()
+
+    if d.xpath('//*[@text="二手车"]').exists:
+
+        d(text='二手车').drag_to(text='综合', timeout=3)
+
+    else:
+
+        d(text='图片').drag_to(text='综合', timeout=3)
+
+    # time.sleep(1)
+    # d.xpath('//*[@text="车友圈"]').click_exists()
+    #
+    # time.sleep(1)
+    # d.xpath('//*[@text="问答"]').click_exists()
+
+    time.sleep(1)
+    d.xpath('//*[@text="用户"]').click()
+
+    time.sleep(3)
+
+    xpath = '//*[@text="{}"]'
+
+    user_name_list = d.xpath(xpath.format(user_name)).all()
+
+    # 根据用户名获取元素 搜索框一定有一个 所以<=1 则表示未搜索到用户
+    if user_name_list.__len__() <= 1:
+        # 当前用户没有获取到任何帖子
+        status = shell_status_content(0, 501, '未搜索到当前用户', '', '')
+        return status
+
+    else:
+        # 懂车帝可能根据搜索算法 推出 是否仍要搜索目标选项, 这种情况对下标为2的进行点击。 正常情况对下标为1的进行点击
+        if not d.xpath('//*[@text="”的搜索结果。仍然搜索:"]').exists:
+            user_name_list[1].click()
+        elif user_name_list.__len__() >= 3:
+            user_name_list[2].click()
+
+    # if user_name_list.__len__() > 1:
+    #     user_name_list[user_name_list.__len__() - 1].click()
+    # else:
+    #     d.xpath(xpath.format(user_name)).click()
+
+    # 点击第一个用户进入首页
+    # d.xpath('//*[@resource-id="root"]/android.view.View[2]/android.view.View[1]/android.view.View[1]').click_exists()
+
+    time.sleep(2)
+
+    # 如果存在关注用户标签 进行关闭
+    check_follow_button(d)
+
+    # 获取帖子发布时间
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/tv_time"]').exists:
+        # 当前用户没有获取到任何帖子
+        status = shell_status_content(0, 501, '当前用户没有获取到任何帖子', '', '')
+
+        return status
+
+    # 预期时间
+    transfer_date = datetime.strptime('2023-10-01', '%Y-%m-%d').date()
+
+    while 1:
+        # 如果存在关注用户标签 进行关闭
+        check_follow_button(d)
+
+        publish_time = d.xpath('//*[@resource-id="com.ss.android.auto:id/tv_time"]').get_text()
+
+        publish_time = __formate_time(publish_time)
+
+        # 发布时间
+        publish_date = datetime.strptime(publish_time, '%Y-%m-%d').date()
+
+        # 获取到所有帖子标题进行模糊匹配
+        text_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').all()
+        for text in text_list:
+            matcher = SequenceMatcher(None, text.text, keyword)
+
+            similarity_ratio = matcher.ratio()
+
+            if similarity_ratio >= 0.8:
+                text.click()
+                loggerKit.info("任务{0},用户{1}下搜索到{2}对应帖子 进行点击并操作", task_id, account_name, keyword)
+                return title_action(d, keyword, account_name, task_id)
+
+        # 如果能够根据标题搜索到帖子
+        # if d.xpath(xpath.format(keyword)).exists:
+        #     d.xpath(xpath.format(keyword)).click()
+        #     return title_action(d, keyword, account_name)
+
+        # 先判断是否超过10月1号
+        if transfer_date > publish_date:
+            # 已经抓取到10月1号帖子 仍未获取到该帖子
+            print('已经获取到10月1号帖子')
+            status = shell_status_content(0, 501, '已经抓取到10月1号帖子 仍未获取到该帖子,不进行下一步获取', '', '')
+
+            return status
+        elif d.xpath('//*[@text="没有更多了"]').exists:
+            # 没有更多帖子
+            print('没有更多了展示')
+            status = shell_status_content(0, 501, "用户拉取到'没有更多了'标识,仍未获取到帖子", '', '')
+
+            return status
+        else:
+            d.swipe_ext("up", 1)
+
+
+def check_follow_button(d):
+    # 如果存在关注用户标签 进行关闭
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/byg"]').exists:
+        d.xpath('//*[@resource-id="com.ss.android.auto:id/byg"]').click()
+
+
+def __formate_time(time_str):
+    now = datetime.now()
+    if '刚刚' in time_str:
+        p_time = now
+    elif '分钟' in time_str:
+        minutes = int(time_str[:time_str.index('分')])
+        p_time = now - timedelta(minutes=minutes)
+    elif '小时' in time_str:
+        hours = int(time_str[:time_str.index('小')])
+        p_time = now - timedelta(hours=hours)
+    elif '昨天' in time_str:
+        p_time = now - timedelta(days=1)
+    elif '前天' in time_str:
+        p_time = now - timedelta(days=2)
+    elif '天' in time_str:
+        days = int(time_str[:time_str.index('天')])
+        p_time = now - timedelta(days=days)
+    elif '周' in time_str:
+        weeks = int(time_str[:time_str.index('周') - 1])
+        p_time = now - timedelta(weeks=weeks)
+    elif '年' not in time_str and '月' in time_str and '日' in time_str:
+        p_time = datetime.strptime(f'{now.year}年{time_str}', "%Y年%m月%d日").date()
+    else:
+        time_str = time_str.split()[0]
+        items = time_str.split("-")
+        if len(items) == 2:
+            p_time = datetime.strptime(f'{now.year}年{items[0]}月{items[1]}日', "%Y年%m月%d日").date()
+        elif len(items) == 3:
+            p_time = datetime.strptime(f'{items[0]}年{items[1]}月{items[2]}日', "%Y年%m月%d日").date()
+        else:
+            return None
+    #
+    p_time = p_time.strftime('%Y-%m-%d')
+    return p_time
+
+
+# 进入帖子进行操作
+def title_action(d, keyword, account_name, task_id):
+    time.sleep(3)
+    # 目前只支持对帖子进行操作
+    if not d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').exists and not d.xpath(
+            '//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        # 可能受网络/帖子内容影响 元素未加载出来 等待三秒再重试加载
+        time.sleep(3)
+        if not retry(lambda: d.xpath('//*[@resource-id="com.tencent.wework:id/ff6"]').exists, task_id):
+            # if not d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').exists:
+            # 获取评论内容,首先获取原文内容如果没有获取到,则获取标题
+            status = shell_status_content(0, 501, "该文章类型不支持操作", '', '')
+            d.app_stop("com.ss.android.auto")
+            return status
+
+    if d.xpath('//*[@text="视频"]').exists:
+        #  返回未找到帖子
+        status = shell_status_content(0, 501, '该帖子为视频类型不支持评论', '', '')
+        d.app_stop("com.ss.android.auto")
+
+        return status
+
+    comment_request_text = ''
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').exists:
+        comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/s"]').get_text()
+
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/izg"]').exists:
+        comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/izg"]').get_text()
+
+    elif d.xpath('//*[@resource-id="com.ss.android.auto:id/p"]').exists:
+        comment_request_text = d.xpath('//*[@resource-id="com.ss.android.auto:id/p"]').get_text()
+    else:
+        d.swipe_ext("up", 0.3)
+
+        text_view_text = ''
+        view_view_text = ''
+
+        # 获取文章
+        article_list = d.xpath('//android.widget.TextView').all()
+        if article_list is None or article_list.__len__() == 0:
+            text_view_text = keyword
+        else:
+            # if article_list.__len__() > 3:
+            #     article_list = article_list[3:]
+            for article in article_list:
+                # if article.text.contains('说点什么'):
+                #     break
+                text_view_text += article.text
+
+        # 获取文章
+        article_list = d.xpath('//android.view.View').all()
+        if article_list is None or article_list.__len__() == 0:
+            view_view_text = keyword
+        else:
+            # if article_list.__len__() > 3:
+            #     article_list = article_list[3:]
+            for article in article_list:
+                # if article.text.contains('说点什么'):
+                #     break
+                view_view_text += article.text
+
+        if len(view_view_text) > len(text_view_text):
+            comment_request_text = view_view_text
+        else:
+            comment_request_text = text_view_text
+
+    request_data = {
+        "templateId": "dcd_comment",
+        "content": comment_request_text
+    }
+
+    response = httpx.post('http://47.116.62.124:3000/comment', json=request_data, timeout=120)
+
+    if not response.is_success:
+        #  调用AIGC获取评论失败
+        status = shell_status_content(0, 500, "调用AIGC获取评论失败", '', '')
+        d.app_stop("com.ss.android.auto")
+        return status
+
+    data = json.loads(response.text)
+
+    reply_text = data.get('text')
+
+    reply_text_json = json.loads(reply_text)
+
+    reply = reply_text_json.get('comment')
+
+    if '内容太少' in reply or '对不起' in reply or reply is None or reply == '':
+        # 返回未获取合适回复内容
+        status = shell_status_content(0, 500, "无效评论,请求为:" + comment_request_text + "回复为:" + reply, '', '')
+        d.app_stop("com.ss.android.auto")
+        return status
+
+    # 点赞
+    if d.xpath('//*[@resource-id="com.ss.android.auto:id/d6l"]').exists:
+        like_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/d6l"]').all()
+        if like_list.__len__() == 1:
+            like_list[0].click()
+        else:
+            like_list[like_list.__len__() - 1].click()
+    else:
+        like_list = d.xpath('//*[@resource-id="com.ss.android.auto:id/ff6"]').all()
+        if like_list.__len__() == 1:
+            like_list[0].click()
+        else:
+            like_list[like_list.__len__() - 1].click()
+
+    time.sleep(2)
+
+    # 点击对话框
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/kl2"]').click()
+
+    time.sleep(2)
+
+    # 评论框赋值
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/daj"]').set_text(reply)
+
+    time.sleep(2)
+
+    # 发布
+    d.xpath('//*[@resource-id="com.ss.android.auto:id/fx_"]').click()
+
+    time.sleep(2)
+
+    if d.xpath('//*[@text="发布评论"]').exists:
+        d.xpath('//*[@text="发布评论"]').click()
+
+    content_result = content_detail(keyword, comment_request_text, reply, account_name)
+
+    status = shell_status_content(1, 0, "", '', json.dumps(content_result.to_dict(), ensure_ascii=False))
+
+    return status
+
+
+# 机器人重复查找元素 针对网络不顺畅的情况 或手机性能问题
+def retry(operation, device_serial, retries=11, retry_interval=3):
+    for i in range(retries):
+
+        loggerKit.info("懂车帝互动获取多次获取元素" + device_serial + "开始操作")
+
+        if operation():
+            return True
+
+        if i == retries - 1:
+            return False
+
+        time.sleep(retry_interval)  # 等待2秒再重试
+
+    return False
+
+
+if __name__ == "__main__":
+    # search_test('QXNUT21905001550','新车  售18.99万元,定位纯电中大型轿车,飞凡F7都市版正式上市')
+    # while 1:
+    #
+    json_str = '''{
+    "data": [
+        {
+            "taskId": 1163119,
+            "mediaChannel": "dongchedi",
+            "taskType": "testPOC",
+            "taskSubType": "dongchediAppointInteraction",
+            "taskSequenceId": "",
+            "taskDesc": "懂车帝指定账号互动",
+            "actionType": "content",
+            "resourceName": "为何飞凡广州车展带来的两道“开胃菜”,让人越吃越“舒适”?",
+            "subResourceName": null,
+            "executeRobotAccount": "testRobotAccount",
+            "executeRobotName": "testRobotAccount",
+            "deviceId": "1",
+            "content": "",
+            "answerType": null,
+            "materialUri": null,
+            "spiderType": null,
+            "sequence": null,
+            "demoTask": false
+        },
+        {
+            "taskId": 1163119,
+            "mediaChannel": "dongchedi",
+            "taskType": "testPOC",
+            "taskSubType": "dongchediAppointInteraction",
+            "taskSequenceId": "",
+            "taskDesc": "懂车帝指定账号互动",
+            "actionType": "content",
+            "resourceName": "为何飞凡广州车展带来的两道“开胃菜”,让人越吃越“舒适”?",
+            "subResourceName": null,
+            "executeRobotAccount": "testRobotAccount",
+            "executeRobotName": "testRobotAccount",
+            "deviceId": "1",
+            "content": "",
+            "answerType": null,
+            "materialUri": null,
+            "spiderType": null,
+            "sequence": null,
+            "demoTask": false
+        }
+    ],
+    "code": "00000000",
+    "message": "success"
+}'''
+
+    data_list = json.loads(json_str).get('data')
+
+    for data in data_list:
+        print(data.get('taskId'))
+
+    status = simulated_operation("7HX5T19911019170", "感受飞凡F7最快的一次充电", 199, 'dongchedi', 'Lightman317')
+
+    loggerKit.info(json.dumps(status, default=lambda o: o.__dict__, indent=4, ensure_ascii=False))

BIN
scene/mock/__pycache__/poc_data.cpython-311.pyc


BIN
scene/oprator/__pycache__/atom_data.cpython-311.pyc


+ 623 - 0
scene/oprator/atom_data.py

@@ -0,0 +1,623 @@
+"""
+启动app
+"""
+
+
+def start_app(request_id, target_app="dongchedi", target_version="7.9.0",
+              package_name="com.ss.android.auto", timeout=10, sleep_time=3):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "start_app",
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+关闭app
+"""
+
+
+def stop_app(request_id, target_app="dongchedi", target_version="7.9.0",
+             package_name="com.ss.android.auto", timeout=10, sleep_time=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "stop_app",
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据控件id单击
+"""
+
+
+def single_click_by_control(request_id, target_app="dongchedi",
+                            target_version="7.9.0",
+                            package_name="com.ss.android.auto",
+                            control_ids="",
+                            control_id="com.ss.android.auto:id/c7p",
+                            item_index=None,
+                            sub_item_index=None,
+                            timeout=10, sleep_time=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "click",
+                "type": "1",
+                "control_id": control_id,
+                "control_ids": control_ids,
+                "item_index": item_index,
+                "sub_item_index": sub_item_index,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据控件id单击
+"""
+
+
+def single_click_by_control_exact(request_id, target_app="dongchedi",
+                                  target_version="7.9.0",
+                                  package_name="com.ss.android.auto",
+                                  control_ids="",
+                                  control_id="com.ss.android.auto:id/c7p",
+                                  item_index=None,
+                                  sub_item_index=None,
+                                  timeout=10, sleep_time=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "exact_click",
+                "type": "1",
+                "control_id": control_id,
+                "control_ids": control_ids,
+                "item_index": item_index,
+                "sub_item_index": sub_item_index,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据屏幕位置点击多次
+"""
+
+
+def many_click_by_position(request_id, target_app="dongchedi",
+                           target_version="7.9.0",
+                           package_name="com.ss.android.auto",
+                           position="center",
+                           count=5,
+                           timeout=10, sleep_time=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "many_click",
+                "type": "1",
+                "position": position,
+                "count": count,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+'''
+根据文本单击
+'''
+
+
+def single_click_by_text(request_id, target_app="dongchedi", target_version="7.9.0", sleep_time=10,
+                         package_name="com.ss.android.auto", text="车友圈", timeout=10, similarity=1, max_page="5"):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "click",
+                "type": "2",
+                "control_text": text,
+                "timeout": timeout,
+                "sleep_time": sleep_time,
+                "similarity": similarity,
+                "max_page": max_page
+            }
+        }
+    }
+
+    return data
+
+
+"""
+通过图片识别后单击
+"""
+
+
+def single_click_by_pic(request_id, target_app="dongchedi", target_version="7.9.0", sleep_time=10,
+                        package_name="com.ss.android.auto", base64_pic="data:image/gif;base64,"
+                                                                       "R0lGODlhHAAmAKIHAKqqqsvLy0hISObm5vf394uLiwAAAP///yH5BEoqQqJKAIBaQOVKHAXr3t7txgBjboSvB8EpLoFZywOAo3LFE5lYs/QW9LT1TRk1V7S2xYJADs=",
+                        timeout=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "click",
+                "type": "3",
+                "base64_pic": base64_pic,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+'''
+滑动手机屏幕的百分比
+'''
+
+
+def swipe_screen(request_id, target_app="dongchedi", target_version="7.9.0", package_name="com.ss.android.auto",
+                 scale=0.5, timeout=10, sleep_time=10, direction="up", scale_from=None, scale_to=None, duration=None):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "swipe",
+                "timeout": timeout,
+                "direction": direction,
+                "scale": scale,
+                "sleep_time": sleep_time,
+                "scaleFrom": scale_from,
+                "scaleTo": scale_to,
+                "duration": duration
+            }
+        }
+    }
+
+    return data
+
+
+'''
+连续滑动手机屏幕的百分比
+'''
+
+
+def continual_swipe_screen(request_id, target_app="dongchedi", target_version="7.9.0",
+                           package_name="com.ss.android.auto",
+                           scale=0.5, timeout=10, sleep_time=10, direction="up", continuous_time=None,
+                           continuous_time_interval=None, continuous_count=None):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "swipe",
+                "timeout": timeout,
+                "direction": direction,
+                "scale": scale,
+                "sleep_time": sleep_time,
+                # 持续时间 单位:s
+                "continuous_time": continuous_time,
+                # 睡眠时间区间(传输一个string类型 区间首尾使用,分割。eg:'3,50')
+                "sleep_time_interval": continuous_time_interval,
+                # 持续次数与continuous_time互斥 都传以continuous_time为主
+                "continuous_count": continuous_count
+            }
+        }
+    }
+
+    return data
+
+
+'''
+根据控件id长按某个控件
+'''
+
+
+def long_press_by_control(request_id, target_app="dongchedi", target_version="7.9.0",
+                          package_name="com.ss.android.auto",
+                          control_id="com.ss.android.auto:id/c7d", timeout=10, sleep_time=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "long_press",
+                "control_id": control_id,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据控件id输入文本(包括emoj表情)
+"""
+
+
+def send_text_by_control(request_id, target_app="dongchedi", target_version="7.9.0",
+                         package_name="com.ss.android.auto", control_id="com.ss.android.auto:id/cg",
+                         content="文章不错,写的很好👍", timeout=10, sleep_time=10, item_index=None):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "send_text",
+                "control_id": control_id,
+                "text": content,
+                "timeout": timeout,
+                "sleep_time": sleep_time,
+                "item_index": item_index
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据控件id获取内容
+如果id/帖子正文内容未获取到 接口返回eleNotFound,包含点击操作
+
+:param
+    type - 1- 帖子正文内容   2- 控件id的文本
+"""
+
+"""
+
+"""
+
+
+def get_content_by_control(request_id, target_app="dongchedi", target_version="7.9.0", operator_type=1, max_page="10",
+                           package_name="com.ss.android.auto", control_id="com.ss.android.auto:id/cg",
+                           timeout=10, sleep_time=10, title="听说24年不送绿牌"):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "spider_content",
+                "type": operator_type,
+                "title": title,
+                "max_page": max_page,
+                "control_id": control_id,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+先下滑若干屏幕
+然后根据匹配到的文本点击
+"""
+
+
+def click_by_content(request_id, target_app="dongchedi", target_version="7.9.0", max_page="10", operator_type=1,
+                     package_name="com.ss.android.auto", timeout=10, sleep_time=10, title="听说24年不送绿牌"):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "spider_content",
+                "title": title,
+                "type": operator_type,
+                "max_page": max_page,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据图片判断页面图片是否存在
+"""
+
+
+def check_pic_exist(request_id, target_app="dongchedi", target_version="7.9.0",
+                    package_name="com.ss.android.auto",
+                    pic_base64="com.ss.android.auto:id/c7p", timeout=10, sleep_time=10, direction="up", swipe_count=0):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "check_pic",
+                "type": "1",
+                "pic_base64": pic_base64,
+                "timeout": timeout,
+                "sleep_time": sleep_time,
+                # (默认up。up 向上滑动(及屏幕向下翻取),down向下滑动(及屏幕向上翻取))
+                "direction": direction,
+                # 滑动次数(默认0 不滑动)
+                "swipe_count": swipe_count
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据图片进行点击
+"""
+
+
+def click_pic(request_id, target_app="dongchedi", target_version="7.9.0",
+              package_name="com.ss.android.auto",
+              pic_base64="com.ss.android.auto:id/c7p", timeout=10, sleep_time=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "click_pic",
+                "type": "1",
+                "pic_base64": pic_base64,
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+操作失败
+"""
+
+
+def operation_fail(request_id, target_app="dongchedi", target_version="7.9.0",
+                   package_name="com.ss.android.auto", step="action0"):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "step": step
+            }
+        }
+    }
+
+    return data
+
+
+"""
+返回上一页
+"""
+
+
+def back_last_page(request_id, target_app="dongchedi", target_version="7.9.0", package_name="com.ss.android.auto",
+                   sleep_time=10,
+                   timeout=10):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "back",
+                "timeout": timeout,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据元素id获取到对应text信息
+
+text信息返回在下一次调用接口result中的performActionText字段返回
+"""
+
+
+def get_text_by_control(request_id, target_app="dongchedi", target_version="7.9.0", max_page="10",
+                        package_name="com.ss.android.auto", control_id="com.ss.android.auto:id/cg",
+                        timeout=10, sleep_time=10, item_index=None, sub_item_index=None):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "spider_text_get",
+                # 最大下滑页
+                "max_page": max_page,
+                "control_id": control_id,
+                "timeout": timeout,
+                "sleep_time": sleep_time,
+                "item_index": item_index,
+                "sub_item_index": sub_item_index
+
+            }
+        }
+    }
+
+    return data
+
+
+"""
+根据元素id获取到对应text信息
+
+text信息返回在下一次调用接口result中的performActionText字段返回
+"""
+
+
+def get_text_by_control_comment(request_id, target_app="dongchedi", target_version="7.9.0", max_page="10",
+                                package_name="com.ss.android.auto", control_id="com.ss.android.auto:id/cg",
+                                timeout=10, sleep_time=10, item_index=None, sub_item_index=None):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "spider_text_list_get",
+                # 最大下滑页
+                "max_page": max_page,
+                "control_id": control_id,
+                "timeout": timeout,
+                "sleep_time": sleep_time,
+                "item_index": item_index,
+                "sub_item_index": sub_item_index
+            }
+        }
+    }
+
+    return data
+
+
+'''
+根据text 点击对应的子元素id
+'''
+
+
+def spider_content_parent_find_click(request_id, target_app="dongchedi", target_version="7.9.0",
+                                     package_name="com.ss.android.auto", title=None,
+                                     child_control_id=None, timeout=10, sleep_time=10, item_index=None):
+    data = {
+        "jsonrpcVersion": "2.0",
+        "id": request_id,
+        "type": "scenes",
+        "target_app": target_app,
+        "package_name": package_name,
+        "target_version": target_version,
+        "params": {
+            "action": {
+                "method": "spider_content_parent_find_click",
+                "child_control_id": child_control_id,
+                "timeout": timeout,
+                "title": title,
+                "item_index": item_index,
+                "sleep_time": sleep_time
+            }
+        }
+    }
+
+    return data

+ 657 - 0
scene/wework/script_wework.py

@@ -0,0 +1,657 @@
+import random
+import threading
+import time
+
+import uiautomator2 as u2
+
+from func.check_device import retry, reboot_device
+from task.task_dict import script_status
+from tools import loggerKit
+
+
+# 4.1.3(21966)
+
+# 编辑群名称
+def wework_edit(device_serial, keyword, content):
+    # 连接设备
+    device = u2.connect(device_serial)
+
+    device.healthcheck()
+    device.screen_on()
+    device.unlock()
+    device.debug = False
+
+    # 重启企业微信
+    device.app_stop("com.tencent.wework")
+
+    device.app_start("com.tencent.wework")
+
+    # 打开搜索框
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click()
+    # 输入搜索文案
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword)
+
+    # 点击搜索所得的第一个
+    device.xpath('//*[@resource-id="com.tencent.wework:id/ge2"]').click()
+
+    # 点击编辑群按钮
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l6w"]').click()
+
+    # 点击群名称
+    device.xpath('//*[@resource-id="com.tencent.wework:id/bze"]').click()
+
+    # 文案设置
+    device.xpath('//*[@resource-id="com.tencent.wework:id/egb"]').set_text(content)
+
+    # 编辑提交
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l73"]').click()
+
+
+# 创建字典来保存锁对象
+lock_dict = {}
+
+sleep_time = 0.5
+
+
+def random_add_subtract(number):
+    operation = random.choice(['add'])
+
+    if operation == 'add':
+        number += random.choice([0.1, 0.2])
+    elif operation == 'subtract':
+        number += random.choice([0.1, 0.2])
+
+    return number
+
+
+# 消息回复
+def wework_reply(device_serial, keyword, content):
+    status = script_status(1, 0, '', '')
+    # 连接设备
+    device = u2.connect(device_serial)
+
+    device.healthcheck()
+    device.screen_on()
+    device.unlock()
+    device.debug = False
+
+    time.sleep(random_add_subtract(sleep_time))
+    # 打开搜索框
+    loggerKit.info('thread[{0}=>{1}], 设备号: {2}, 打开搜索框', threading.current_thread().name, threading.get_ident(),
+                   device_serial)
+    start_time1 = time.time()
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click()
+    else:
+        return retry(lambda: device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click(), device_serial,
+                     "搜索按钮元素未获取到")
+    end_time1 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 打开搜索框==>>执行时长:{3}", threading.current_thread().name,
+                   threading.get_ident(), device_serial, end_time1 - start_time1)
+
+    time.sleep(random_add_subtract(sleep_time))
+
+    # 输入搜索文案
+    loggerKit.info('thread[{0}=>{1}], 输入搜索文案:{2}, 设备号:{3}', threading.current_thread().name,
+                   threading.get_ident(),
+                   keyword, device_serial)
+    start_time2 = time.time()
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword)
+    else:
+        return retry(lambda: device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword),
+                     device_serial,
+                     "搜索框元素未获取到")
+    end_time2 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 输入搜索文案==>>执行时长:{3}", threading.current_thread().name,
+                   threading.get_ident(), device_serial, end_time2 - start_time2)
+
+    time.sleep(random_add_subtract(sleep_time))
+
+    loggerKit.info('thread[{0}=>{1}], 点击搜索所得的第一个', threading.current_thread().name, threading.get_ident())
+    start_time3 = time.time()
+    time.sleep(1)
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/kzx"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/ge2"]').click()
+    else:
+        return retry(lambda: device.xpath('//*[@resource-id="com.tencent.wework:id/ge2"]').click(), device_serial,
+                     "搜索不到群聊")
+    end_time3 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 点击搜索所得的第一个==>>执行时长:{3}",
+                   threading.current_thread().name,
+                   threading.get_ident(), device_serial, end_time3 - start_time3)
+
+    time.sleep(random_add_subtract(sleep_time))
+
+    # 编辑框
+    loggerKit.info('thread[{0}=>{1}], 设备号:{2}, 输入编辑框文本', threading.current_thread().name,
+                   threading.get_ident(), device_serial)
+    start_time4 = time.time()
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').click()
+    else:
+        return retry(lambda: device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').click(), device_serial,
+                     "编辑框元素未获取到")
+    end_time4 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 编辑框==>>执行时长:{3}", threading.current_thread().name,
+                   threading.get_ident(), device_serial, end_time4 - start_time4)
+    time.sleep(random_add_subtract(sleep_time))
+
+    # 内容赋值
+    text = content.replace("<br/>", "\n")
+    # 直接输出替换后的字符串
+    loggerKit.info('替换后的字符串:{0}', text)
+    # 内容赋值
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').set_text(text)
+
+    # 发送
+    loggerKit.info('thread[{0}=>{1}], 设备号:{2}, 发送文案', threading.current_thread().name, threading.get_ident(),
+                   device_serial)
+    start_time5 = time.time()
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').click()
+    else:
+        return retry(lambda: device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').click(), device_serial,
+                     "发送按钮元素未获取到")
+    end_time5 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 发送==>>执行时长:{3}", threading.current_thread().name,
+                   threading.get_ident(), device_serial, end_time5 - start_time5)
+
+    time.sleep(random_add_subtract(sleep_time))
+
+    # 退出会话
+    loggerKit.info('thread[{0}=>{1}], 设备号:{2}, 退出会话', threading.current_thread().name, threading.get_ident(),
+                   device_serial)
+    time.sleep(random_add_subtract(sleep_time))
+    start_time6 = time.time()
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+    end_time6 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 退出会话==>>执行时长:{3}", threading.current_thread().name,
+                   threading.get_ident(),
+                   device_serial, end_time6 - start_time6)
+
+    # 退出搜索
+    loggerKit.info('thread[{0}=>{1}], 设备号:{2}, 退出搜索', threading.current_thread().name, threading.get_ident(),
+                   device_serial)
+    time.sleep(random_add_subtract(sleep_time))
+
+    start_time7 = time.time()
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+    end_time7 = time.time()
+    loggerKit.info("thread[{0}=>{1}], 设备号:{2}, 退出搜索==>>执行时长:{3}", threading.current_thread().name,
+                   threading.get_ident(),
+                   device_serial, end_time7 - start_time7)
+
+    return status
+
+
+# 指定人消息回复
+def wework_appoint_reply(device_serial, keyword, username, content):
+    # 连接设备
+    device = u2.connect(device_serial)
+    status = script_status(1, 0, '', '')
+
+    # 打开搜索框
+    loggerKit.info('thread[{0}=>{1}], 设备号: {2}, 打开搜索框', threading.current_thread().name, threading.get_ident(),
+                   device_serial)
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click()
+    else:
+        status = script_status(0, 500, '搜索按钮元素未获取到', '')
+        reboot_device(device_serial, '搜索按钮元素未获取到')
+        return status
+    time.sleep(random_add_subtract(sleep_time))
+
+    # 输入搜索文案
+    loggerKit.info('thread[{0}=>{1}], 输入搜索文案:{2}, 设备号:{3}', threading.current_thread().name,
+                   threading.get_ident(),
+                   keyword, device_serial)
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword)
+    else:
+        status = script_status(0, 500, '搜索框元素未获取到', '')
+        reboot_device(device_serial, '搜索框元素未获取到')
+        return status
+
+    time.sleep(1)
+    # 点击搜索所得的第一个
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/kzx"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/ge2"]').click()
+    else:
+        status = script_status(0, 500, '搜索不到群聊', '')
+        reboot_device(device_serial, '搜索不到群聊')
+        return status
+
+    time.sleep(random_add_subtract(sleep_time))
+
+    # 编辑框
+    loggerKit.info('thread[{0}=>{1}], 设备号:{2}, 输入编辑框文本', threading.current_thread().name,
+                   threading.get_ident(),
+                   device_serial)
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').click()
+        # 内容赋值
+        edit = device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]')
+
+        edit.set_text("@")
+        time.sleep(random_add_subtract(sleep_time))
+
+        if device.xpath('//*[@resource-id="com.tencent.wework:id/dae"]').exists:
+            time.sleep(random_add_subtract(sleep_time))
+            # 搜索框赋值 @出指定人
+            device.xpath('//*[@resource-id="com.tencent.wework:id/dae"]').set_text(username)
+            # 因网络原因 部分随机等待时间可能会对程序有影响
+            time.sleep(random_add_subtract(sleep_time))
+            # 获取到搜索出的第一个人
+            if device.xpath('//*[@resource-id="com.tencent.wework:id/itp"]/android.view.ViewGroup[1]').exists:
+                device.xpath('//*[@resource-id="com.tencent.wework:id/itp"]/android.view.ViewGroup[1]').click()
+            else:
+                status = script_status(0, 500, '@不到指定人', '')
+                reboot_device(device_serial, '@不到指定人')
+                return status
+            # 内容赋值
+            # 使用 replace() 方法将字符串中的 <br/> 替换成 \n
+            text = content.replace("<br/>", "\n")
+            edit.set_text(text + "\n" + edit.text)
+        else:
+            status = script_status(0, 500, '@指定人元素未获取到', '')
+            reboot_device(device_serial, '@指定人元素未获取到')
+            return status
+    else:
+        status = script_status(0, 500, '编辑框元素未获取到', '')
+        reboot_device(device_serial, '编辑框元素未获取到')
+        return status
+
+    # 发送
+    loggerKit.info('thread[{0}=>{1}], 设备号:{2}, 发送文案', threading.current_thread().name, threading.get_ident(),
+                   device_serial)
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').click()
+    else:
+        status = script_status(0, 500, '发送按钮元素未获取到', '')
+        reboot_device(device_serial, '发送按钮元素未获取到')
+        return status
+
+    # 退出会话
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+    # 退出搜索
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    return status
+
+
+# 企微群消息回复+h5 转发操作
+def wework_reply_h5(device_serial, keyword, content, material_id):
+    status = wework_reply(device_serial, keyword, content)
+    if status.execute_status == 0:
+        return status
+    status = wework_h5(device_serial, keyword, material_id)
+
+    return status
+
+
+# 企微指定人群消息回复 +h5 转发操作
+def wework_appoint_reply_h5(device_serial, keyword, username, content, material_id):
+    status = wework_appoint_reply(device_serial, keyword, username, content)
+    if status.execute_status == 0:
+        return status
+    status = wework_h5(device_serial, keyword, material_id)
+    return status
+
+
+# 企微群消息回复+h5 转发操作
+def wework_reply_mini(device_serial, keyword, content, material_id):
+    status = wework_reply(device_serial, keyword, content)
+    if status.execute_status == 0:
+        return status
+    status = wework_mini(device_serial, keyword, material_id)
+
+    return status
+
+
+# 企微指定人群消息回复 +h5 转发操作
+def wework_appoint_reply_mini(device_serial, keyword, username, content, material_id):
+    status = wework_appoint_reply(device_serial, keyword, username, content)
+    if status.execute_status == 0:
+        return status
+    status = wework_mini(device_serial, keyword, material_id)
+    return status
+
+
+# 企微群转发h5 操作
+def wework_h5(device_serial, keyword, material_id):
+    status = script_status(1, 0, '', '')
+    # 设备连接
+    d = u2.connect(device_serial)
+
+    # 搜索
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click_exists()
+
+    # 关键词
+    d.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').click_exists()
+    d(focused=True).set_text("qa-机器人素材")
+
+    # 点击
+    d.xpath(
+        '//*[@resource-id="com.tencent.wework:id/jh1"]/android.widget.RelativeLayout[2]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout['
+        '1]/android.widget.RelativeLayout[1]').click_exists()
+
+    # 加载等待
+    time.sleep(5)
+
+    # 点解编辑按钮
+    d.click(0.402, 0.18)
+
+    # 设置url
+    d.send_keys(material_id)
+
+    # 点击h5跳转
+    d.click(0.563, 0.262)
+
+    # 进行转发操作
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l6t"]').click()
+
+    d.xpath('//*[@text="转发"]').click()
+
+    # 搜索转发人
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l71"]').click()
+
+    # 搜索框赋值
+    d.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword)
+
+    time.sleep(1)
+    # 点击第一个联系人
+    d.xpath(
+        '//*[@resource-id="com.tencent.wework:id/c6f"]/android.widget.RelativeLayout[2]').click()
+
+    # 点击发送按钮
+    d.xpath('//*[@resource-id="com.tencent.wework:id/ck0"]').click()
+
+    # 退出
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    # 返回首页
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    return status
+
+
+# 企微群转发h5 操作
+def wework_mini(device_serial, keyword, material_id):
+    status = script_status(1, 0, '', '')
+    # 设备连接
+    d = u2.connect(device_serial)
+
+    # 搜索
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click_exists()
+
+    # 关键词
+    d.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').click_exists()
+    d(focused=True).set_text("qa-机器人素材")
+
+    # 点击
+    d.xpath(
+        '//*[@resource-id="com.tencent.wework:id/jh1"]/android.widget.RelativeLayout['
+        '2]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout['
+        '1]/android.widget.RelativeLayout[1]').click_exists()
+
+    # 加载等待
+    time.sleep(5)
+
+    # 点解编辑按钮
+    d.click(0.402, 0.18)
+
+    # 设置url
+    d.send_keys(material_id)
+
+    # 点击小程序跳转
+    d.click(0.563, 0.334)
+
+    # 进行转发操作
+    d.xpath('//*[@content-desc="更多"]').click()
+
+    time.sleep(2)
+    # 进入小程序前可能会有加载时间
+
+    if d.xpath('//*[@text="转发"]').exists:
+        d.xpath('//*[@text="转发"]').click()
+    else:
+        status = script_status(0, 500, '无法打开小程序' + material_id, '')
+        return status
+
+    # 搜索转发人
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l71"]').click()
+
+    # 搜索框赋值
+    d.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword)
+
+    time.sleep(1)
+    # 点击第一个联系人
+    d.xpath(
+        '//*[@resource-id="com.tencent.wework:id/c6f"]/android.widget.RelativeLayout[2]').click()
+
+    # 点击发送按钮
+    d.xpath('//*[@resource-id="com.tencent.wework:id/ck0"]').click()
+
+    # 退出
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    # 返回首页
+    d.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    return status
+
+
+# 训练营
+def wework_camp(device_serial, keyword):
+    # 连接设备
+    device = u2.connect(device_serial)
+
+    device.healthcheck()
+    device.screen_on()
+    device.unlock()
+    device.debug = False
+
+    # 重启企业微信
+    device.app_stop("com.tencent.wework")
+
+    device.app_start("com.tencent.wework")
+
+    # 打开搜索框
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click()
+
+    # 输入搜索文案
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(keyword)
+
+    # 点击搜索所得的第一个
+    device.xpath('//*[@resource-id="com.tencent.wework:id/ge2"]').click()
+
+    while 1:
+
+        # 如果有训练营按钮 点击进入
+        if device.xpath('//*[@text="成长营"]').exists:
+            device.xpath('//*[@text="成长营"]').click_exists(10)
+            # 缓冲五秒
+            time.sleep(5)
+            # h5页面无法获取到对应的xpath元素  进行坐标点击
+            device.click(0.168, 0.152)
+            time.sleep(2)
+            # 发送按钮点击
+            device.xpath('//*[@resource-id="com.tencent.wework:id/ck0"]').click()
+            break
+        # 如果有添加xpath 进入添加工具栏页面
+        if device.xpath('//*[@resource-id="com.tencent.wework:id/ham"]').exists:
+            device.xpath('//*[@resource-id="com.tencent.wework:id/ham"]').click()
+
+            break
+        # 向右滑动继续获取
+        parent_view = device.xpath('//*[@resource-id="com.tencent.wework:id/fbf"]')
+        parent_view.swipe("left")
+
+
+def invite_demo(device_serial, key_word, user_name, content):
+    # 编辑群页面最多展示的群人数
+    max_count = 20
+
+    add_people_xpath = '//*[@resource-id="com.tencent.wework:id/edb"]/android.widget.LinearLayout[{}]/android.widget.ImageView[1]'
+
+    # 设备连接
+    device = u2.connect(device_serial)
+
+    # 重启企业微信
+    device.app_stop("com.tencent.wework")
+
+    device.app_start("com.tencent.wework")
+
+    # 打开搜索框
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l76"]').click()
+
+    # 输入搜索文案
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(key_word)
+
+    # 点击搜索所得的第一个
+    device.xpath('//*[@resource-id="com.tencent.wework:id/ge2"]').click()
+
+    # 点击编辑群按钮
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l6w"]').click()
+
+    # 获取群人数
+    people_count = device.xpath('//*[@resource-id="com.tencent.wework:id/eb_"]').get_text()
+
+    # 获取到群人数
+    people_count = int(people_count.replace("人", ""))
+
+    if people_count >= max_count:
+        people_count = max_count
+    else:
+        people_count = people_count + 1
+
+    add_people_xpath = add_people_xpath.format(people_count)
+
+    # 进入选择联系人页面
+    device.xpath(add_people_xpath).click()
+
+    # 选择企业通讯录
+    device.xpath('//*[@text="企业通讯录"]').click()
+
+    # 点击搜索按钮
+    device.xpath(
+        '//*[@resource-id="com.tencent.wework:id/jlj"]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.LinearLayout[2]/android.widget.RelativeLayout[1]/android.widget.TextView[1]').click()
+
+    # 搜索需添加人员
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(user_name)
+
+    # 选中第一个匹配人员
+    device.xpath(
+        '//*[@resource-id="com.tencent.wework:id/jlj"]/android.widget.RelativeLayout[1]/android.widget.ListView[1]/android.widget.RelativeLayout[2]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[2]').click()
+
+    # 确定添加人员
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/jlf"]').exists:
+        # 如果有确认按钮 确保该群中之前没有该用户
+        device.xpath('//*[@resource-id="com.tencent.wework:id/jlf"]').click()
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+    else:
+        # 如果没有确认按钮  确保该群众已经有了该用户
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+        device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    # 编辑框
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').click()
+
+    # 内容赋值
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').set_text(content)
+
+    # 发送
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').click()
+
+    # 点击编辑群按钮
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l6w"]').click()
+
+    # 退出群聊
+    while 1:
+        if device.xpath('//*[@resource-id="com.tencent.wework:id/jtt"]').exists:
+            device.xpath('//*[@resource-id="com.tencent.wework:id/jtt"]').click()
+            break
+        else:
+            device.swipe_ext('up', scale=1)
+
+    # 确定退出群聊
+    device.xpath('//*[@resource-id="com.tencent.wework:id/b42"]').click()
+
+
+def create_group(device_serial, robot_name, customer_name, group_name, content):
+    # 设备连接
+    device = u2.connect(device_serial)
+
+    # 打开添加按钮
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l6w"]').click()
+
+    # 点击发起群聊按钮
+    device.xpath('//*[@text="发起群聊"]').click()
+
+    # 点击搜索按钮 开始搜索人员
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l7b"]').click()
+
+    # 输入机器人姓名
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(robot_name)
+
+    time.sleep(1)
+    if device.xpath(
+            '//*[@resource-id="com.tencent.wework:id/itp"]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.ImageView[1]').exists:
+        device.xpath(
+            '//*[@resource-id="com.tencent.wework:id/itp"]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.ImageView[1]').click()
+
+    # 重置搜索框
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfh"]').click()
+
+    # 输入机器人姓名
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jfi"]').set_text(customer_name)
+    time.sleep(1)
+
+    # 输入客户名称
+    if device.xpath('//*[@resource-id="com.tencent.wework:id/jk0"]').exists:
+        device.xpath('//*[@resource-id="com.tencent.wework:id/jk0"]').click()
+
+    # 点击确认按钮
+    device.xpath('//*[@resource-id="com.tencent.wework:id/jjx"]').click()
+
+    # 点击编辑群信息
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l6w"]').click()
+
+    # 点击群聊名称
+    device.xpath('//*[@text="群聊名称"]').click()
+
+    # 设置群名称
+    device.xpath('//*[@resource-id="com.tencent.wework:id/egb"]').set_text(group_name)
+
+    # 确定修改
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l73"]').click()
+
+    # 退出群编辑
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l67"]').click()
+
+    # 编辑框
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').click()
+
+    # 内容赋值
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsj"]').set_text(content)
+
+    # 发送
+    device.xpath('//*[@resource-id="com.tencent.wework:id/gsf"]').click()
+
+    # 点击编辑群按钮
+    device.xpath('//*[@resource-id="com.tencent.wework:id/l6w"]').click()
+
+    # 退出群聊
+    while 1:
+        if device.xpath('//*[@resource-id="com.tencent.wework:id/jtt"]').exists:
+            device.xpath('//*[@resource-id="com.tencent.wework:id/jtt"]').click()
+            break
+        else:
+            device.swipe_ext('up', scale=1)
+
+    # 确定退出群聊
+    device.xpath('//*[@resource-id="com.tencent.wework:id/b42"]').click()

BIN
sign.png


BIN
sign_ready.png


+ 8 - 0
sources.list

@@ -0,0 +1,8 @@
+#deb https://mirrors.aliyun.com/debian/ bookworm main non-free non-free-firmware contrib
+#deb-src https://mirrors.aliyun.com/debian/ bookworm main non-free non-free-firmware contrib
+deb https://mirrors.aliyun.com/debian-security/ bookworm-security main
+deb-src https://mirrors.aliyun.com/debian-security/ bookworm-security main
+#deb https://mirrors.aliyun.com/debian/ bookworm-updates main non-free non-free-firmware contrib
+#deb-src https://mirrors.aliyun.com/debian/ bookworm-updates main non-free non-free-firmware contrib
+#deb https://mirrors.aliyun.com/debian/ bookworm-backports main non-free non-free-firmware contrib
+#deb-src https://mirrors.aliyun.com/debian/ bookworm-backports main non-free non-free-firmware contrib

+ 0 - 0
strategy/__init__.py


BIN
strategy/__pycache__/__init__.cpython-311.pyc


BIN
strategy/__pycache__/rpa_enum.cpython-311.pyc


BIN
strategy/__pycache__/rpa_handler.cpython-311.pyc


+ 333 - 0
strategy/rpa_atom_handler.py

@@ -0,0 +1,333 @@
+import json
+
+from cubic.cubic_simple_action import cubic_simple_action
+from dongchedi.simple_comment import simple_comment
+from douyin.douyin_officail_account_interact import douyin_random_watch_video
+from kuaishou.gifmaker_random_watch_video import gifmaker_random_watch_video
+from tools import loggerKit
+from strategy.rpa_enum import media_type, task_sub_type, task_key, task_type
+
+from douyin.douyin_random_search import douyin_random_search
+from douyin.simple_browser_video import douyin_spider as douyin_sign_browser_video
+from douyin.douyin_sign import douyin_spider as douyin_sign
+from douyin.douyin_watch_advise import douyin_spider as douyin_watch_advise
+from douyin.douyin_boxes_advise import douyin_spider as douyin_boxes_advise
+from douyin.douyin_user_operate import douyin_spider as douyin_user_demo
+from douyin.douyin_train_home_comment import douyin_spider as douyin_train_home_comment
+from douyin.douyin_interaction_urgent import douyin_spider as douyin_interact_urgent
+from douyin.douyin_live_comment import douyin_spider as douyin_live_comment
+from douyin.douyin_train_home_page import douyin_spider as douyin_train_home_page
+from douyin.douyin_comments_operate import douyin_spider as douyin_comments_operate
+
+from dongchedi.random_browse import random_browse as dongchedi_content_interaction
+from dongchedi.dongchedi_sign import dongchedi_sign as dongchedi_sign
+
+from toutiao.search_double import search_task as search_task
+from toutiao.search_random import search_random_task as search_random_task
+from toutiao.watch_novel import watch_novel_task as watch_novel_task
+from toutiao.meal_allowance import meal_allowance_task as meal_allowance_task
+from xiaohongshu.xingin_random_watch_video import xingin_random_watch_video
+from yiche.yiche_direction_action import yiche_direction_action
+
+
+# 定义策略过程
+class rpa_processor_strategy:
+    def process_rpa(self, task):
+        pass
+
+
+# 抖音看视频领金币操作
+class douyin_browser_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_sign_browser_video(task.device_id, task.task_id, task.resource_name, task.media_channel,
+                                         task.request_data)
+
+
+# 抖音签到操作
+class douyin_sign_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_sign(task.device_id, task.execute_robot_name, task.task_id, task.resource_name,
+                           task.media_channel, task.request_data)
+
+
+# 抖音看广告操作
+class douyin_watch_advise_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_watch_advise(task.device_id, task.task_id, task.resource_name, task.media_channel,
+                                   task.request_data)
+
+
+# 抖音开宝箱+看广告操作
+class douyin_boxes_advise_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_boxes_advise(task.device_id, task.execute_robot_name, task.task_id,
+                                   task.resource_name, task.media_channel, task.request_data)
+
+
+# 懂车帝随机养号
+class dongchedi_content_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝养号脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return dongchedi_content_interaction(task.task_id, task.execute_robot_name, task.request_data)
+
+
+# 头条搜一搜赚钱任务
+class toutiao_search_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条搜一搜赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return search_task(task.task_id, task.request_data)
+
+
+# 头条随机搜赚钱任务
+class toutiao_search_random_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条搜一搜赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return search_random_task(task.task_id, task.request_data)
+
+
+# 头条看小说赚钱任务
+class toutiao_watch_novel_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条看小说赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return watch_novel_task(task.task_id, task.request_data)
+
+
+# 头条吃饭补贴任务
+class toutiao_meal_allwance_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条吃饭补贴脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return meal_allowance_task(task.task_id, task.request_data)
+
+
+# 抖音用户demo任务
+class douyin_user_demo_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音搜一搜赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_user_demo(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+抖音极速版
+随机搜索
+"""
+
+
+class douyin_random_search_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音随机搜索操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+
+        return douyin_random_search(task.task_id, task.resource_name, task.request_data)
+
+
+# 懂车帝签到操作
+class dongchedi_sign_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝签到操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return dongchedi_sign(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+懂车帝简单互动
+"""
+
+
+class dongchedi_simple_comment(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝简单互动操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return simple_comment(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+快手简单养号
+"""
+
+
+class kuaishou_random_watch_video_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行快手简单养号操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return gifmaker_random_watch_video(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+小红书简单养号
+"""
+
+
+class xiaohongshu_random_watch_video_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行快手简单养号操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return xingin_random_watch_video(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+易车官媒
+"""
+
+
+class yiche_direction_action_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行易车官媒操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return yiche_direction_action(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+汽车之家官媒
+"""
+
+
+class cubic_simple_action_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行汽车之家官媒操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return cubic_simple_action(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+抖音常规版官媒
+"""
+
+
+class douyin_random_watch_video_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行快手简单养号操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_random_watch_video(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音养号首页(浏览30 - 50视频随机时长和点赞收藏)
+class douyin_train_home_page_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音养号首页操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_train_home_page(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音养号首页帖子评论(浏览30-50帖子 随机时长 对其评论进行观看)
+class douyin_train_home_comment_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音养号首页帖子评论操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_train_home_comment(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音紧急互动
+class douyin_urgent_interact_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音紧急互动,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_interact_urgent(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音直播间操作
+class douyin_live_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音直播间操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_live_comment(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音评论点赞操作
+class douyin_comment_like_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音评论点赞操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_comments_operate(task.task_id, task.resource_name, task.request_data)
+
+
+# 定义上下文类
+class rpa_processor:
+    def __init__(self, strategy):
+        self.strategy = strategy
+
+    def set_strategy(self, strategy):
+        self.strategy = strategy
+
+    def process_rpa(self, task):
+        return self.strategy.process_rpa(task)
+
+
+class rpa_handler:
+    @staticmethod
+    def set_processor(task):
+        loggerKit.info(
+            'media_channel:{0}, action_type:{1},answer_type:{2}, task_type:{3} task_sub_type:{4} ,task_id:{5}',
+            task.media_channel, task.action_type,
+            task.answer_type, task.task_type, task.task_sub_type, task.task_id)
+
+        default_strategy = None  # 默认策略类
+
+        # 添加其他映射关系
+        strategy_mapping = {
+            # (media_type.douyin.value, task_sub_type.douyin_sign_browser_video.value): douyin_browser_operation(),
+            # (media_type.douyin.value, task_sub_type.douyin_sign.value): douyin_sign_operation(),
+            # (media_type.douyin.value, task_sub_type.douyin_watch_advise.value): douyin_watch_advise_operation(),
+            # (media_type.douyin.value, task_sub_type.douyin_boxes_advise.value): douyin_boxes_advise_operation(),
+            (task_type.dongchediAccountBoosting.value,
+             task_key.DCD_RANDOM_BROWSE.value): dongchedi_content_interaction_operation(),
+            # (media_type.toutiao.value,
+            #  task_sub_type.toutiao_search_task.value): toutiao_search_task_interaction_operation(),
+            # (media_type.toutiao.value,
+            #  task_sub_type.toutiao_search_random_task.value): toutiao_search_random_task_interaction_operation(),
+            # (media_type.toutiao.value,
+            #  task_sub_type.toutiao_watch_novel_task.value): toutiao_watch_novel_task_interaction_operation(),
+            # (media_type.toutiao.value,
+            #  task_sub_type.toutiao_meal_allowance_task.value): toutiao_meal_allwance_task_interaction_operation(),
+            # (media_type.douyin.value, task_sub_type.douyin_user_demo.value): douyin_user_demo_operation(),
+            # (media_type.dongchedi.value, task_sub_type.dongchedi_sign.value): dongchedi_sign_operation(),
+            (task_type.douyinAccountBoosting.value, task_key.DY_SEARCH_RANDOM_BROWSE.value): douyin_random_search_operation(),
+            (task_type.douyinAccountBoosting.value,
+             task_key.DY_RANDOM_BROWSE_VIEW_COMMENTS.value): douyin_train_home_comment_operation(),
+            (task_type.douyinAccountBoosting.value, task_key.DY_RANDOM_BROWSE_LIKE_COLLECTION.value): douyin_train_home_page_operation(),
+            (task_type.douyinCommentInteraction.value, task_key.DY_SPECIFIC_TRANSFER_LIKE_COMMENT.value): douyin_urgent_interact_operation(),
+            (task_type.dongchediManyOperationInteraction.value,
+             task_key.DCD_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT.value): dongchedi_simple_comment(),
+            # (media_type.kuaishou.value,
+            #  task_sub_type.kuaishou_train_home.value): kuaishou_random_watch_video_operation(),
+            # (media_type.xiaohongshu.value,
+            #  task_sub_type.xiaohongshu.xiaohongshu_train_home.value): xiaohongshu_random_watch_video_operation(),
+            (task_type.douyinOfficialMediaInteraction.value,
+             task_key.DY_OFFICIAL_MEDIA.value): douyin_random_watch_video_operation(),
+            (task_type.yicheOfficialMediaInteraction.value,
+             task_key.YC_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT.value): yiche_direction_action_operation(),
+            (task_type.qichezhijiaOfficialMediaInteraction.value,
+             task_key.QCZJ_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT.value): cubic_simple_action_operation(),
+            (task_type.douyinDirectorInteraction.value,
+             task_key.DY_COMING_BROADCASTING_ROOM.value): douyin_live_operation(),
+            (task_type.douyinCommentLikeOperate.value,
+             task_key.DY_COMMENT_LIKE_OPERATE.value): douyin_comment_like_operation()
+        }
+
+        # 根据task.action_type和task.answer_type获取相应的策略类
+        strategy = strategy_mapping.get((task.task_type, task.task_key), default_strategy)
+
+        if strategy is None:
+            # 处理没有匹配到任何策略类的情况
+            raise ValueError("No strategy found for the given task")
+
+        we_strategy = rpa_processor(strategy)
+        return we_strategy

+ 230 - 0
strategy/rpa_enum.py

@@ -0,0 +1,230 @@
+from enum import Enum
+
+
+class task_type(Enum):
+    # 微信
+    contentWellKnown = 'contentWellKnown'
+
+    # 懂车帝互动
+    dongchediManyOperationInteraction = 'dongchedi_many_operation_interaction'
+
+    # 懂车帝养号
+    dongchediAccountBoosting = 'dongchedi_account_boosting'
+
+    # 抖音直播间
+    douyinDirectorInteraction = 'douyin_director_interaction'
+
+    # 抖音特定内容评论
+    douyinCommentInteraction = 'douyin_comment_interaction'
+
+    # 抖音官媒任务
+    douyinOfficialMediaInteraction = 'douyin_official_media_interaction'
+
+    # 抖音养号任务
+    douyinAccountBoosting = 'douyin_account_boosting'
+
+    # 汽车之家官媒
+    qichezhijiaOfficialMediaInteraction = 'qichezhijia_official_media_interaction'
+
+    # 易车官媒
+    yicheOfficialMediaInteraction = 'yiche_official_media_interaction'
+
+    # 抖音评论点赞操作
+    douyinCommentLikeOperate = 'douyin_comment_like_operate'
+
+
+# 操作平台
+class media_type(Enum):
+    # 抖音
+    douyin = 'douyin'
+    # 懂车帝
+    dongchedi = 'dongchedi'
+    # 头条
+    toutiao = 'toutiao'
+    # 快手
+    kuaishou = 'kuaishou'
+    # 小红书
+    xiaohongshu = 'xiaohongshu'
+    # 易车
+    yiche = 'yiche'
+    # 汽车之家
+    auto_home = 'autohome'
+    # 抖音官媒
+    douyin_office_media = 'douyinOfficeMedia'
+    # 易车
+    yi_che_office_media = 'yicheOfficeMedia'
+    # 汽车之家
+    auto_home_office_media = 'autoHomeOfficeMedia'
+
+
+# 任务子类型
+class task_sub_type(Enum):
+    # 懂车帝账号爬取
+    dongchedi = 'dongchediAccount'
+
+    # 小红书账号爬取
+    xiaohongshu = 'xiaohongshuAccount'
+
+    # 快手
+    kuaishou_train_account = 'kuaishouTrainAccount'
+
+    # 快手随机浏览
+    kuaishou_train_home = 'kuaishouTrainHome'
+
+    # 小红书
+    xiaohongshu_train_account = 'xiaohongshuTrainAccount'
+
+    # 小红书随机浏览
+    xiaohongshu_train_home = 'xiaohongshuTrainHome'
+
+    # 懂车帝简单互动
+    dongchedi_comment = 'dongchediComment'
+
+    dongchedi_dppoint_interaction = 'dongchediAppointInteraction'
+
+    dongchediDevelopAccount = 'dongchediDevelopAccount'
+
+    # 新出行
+    xinchuxingDevelopAccount = 'newTravelAccount'
+
+    # 懂车帝R7车友圈
+    cheyouquan_r7 = 'cheyouquanR7Data'
+
+    # 懂车帝F7车友圈
+    cheyouquan_f7 = 'cheyouquanF7Data'
+
+    # 天眼查
+    tianyancha = 'tianyanchaData'
+
+    # 懂车帝排行榜
+    dongchedi_rank = 'dongchediIndex'
+
+    # 汽车之家排行榜
+    autohome_rank = 'qichezhijiaIndex'
+
+    # 微信排行榜
+    wechat_rank = 'wechatIndex'
+
+    # 抖音排行榜
+    douyin_rank = 'douyinIndex'
+
+    # 抖音官媒
+    douyin_office_media_operator = 'douyinOfficeMediaOperator'
+
+    douyin_account_data = 'douyinAccountData'
+
+    # 易车官媒
+    yi_che_office_media_operator = 'yicheOfficeMediaOperator'
+
+    # 汽车之家官媒
+    auto_home_office_media_operator = 'autoHomeOfficeMediaOperator'
+
+    # 新出行R7社区
+    community_r7 = 'communityR7Data'
+
+    # 新出行F7社区
+    community_f7 = 'communityF7Data'
+
+    # 无线控制抖音subType
+    douyin_sub_type = 'testPOC1'
+
+    # 无线控制懂车帝养号subType
+    dongchedi_culture_sub_type = 'dongchediDevelopAccount'
+
+    # 无线控制懂车帝互动subType
+    dongchedi_interaction_sub_type = 'dongchediAppointInteraction'
+
+    # 无线控制手机保活操作
+    keep_alive_sub_type = 'keepAlive'
+
+    # 抖音日常滑动视频
+    douyin_sign_browser_video = 'douyinSignBrowserVideo'
+
+    # 抖音签到
+    douyin_sign = 'douyinSign'
+
+    # 抖音看广告领取金币
+    douyin_watch_advise = 'douyinWatchAdvise'
+
+    # 抖音开宝箱+看广告
+    douyin_boxes_advise = 'douyinOpenTreasureByWatchingAd'
+
+    # 抖音看小说
+    douyin_watch_noval = 'douyinWatchNoval'
+
+    # 懂车帝内容交互
+    content_interaction = 'trainAccount'
+
+    # 头条搜一搜赚钱
+    toutiao_search_task = 'toutiaoSearchTask'
+
+    # 头条随机搜赚钱
+    toutiao_search_random_task = 'toutiaoSearchRandomTask'
+
+    # 头条看小说赚钱
+    toutiao_watch_novel_task = 'toutiaoWatchNovelTask'
+
+    # 头条吃饭补贴
+    toutiao_meal_allowance_task = 'toutiaomealallowenceTask'
+
+    # 抖音用户demo
+    douyin_user_demo = 'douyinUserDemo'
+
+    # 抖音极速版搜索关键词,浏览对应视频随机时长
+    douyin_random_search = "douyinTrainSearchPage"
+
+    # 懂车帝签到
+    dongchedi_sign = 'dongchediSign'
+
+    # 抖音养号首页(浏览30 - 50视频随机时长和点赞收藏)
+    douyin_train_home_page = 'douyinTrainHomePage'
+
+    # 抖音养号搜索页(搜索关键词,浏览对应视频随机时长)
+    douyin_train_search_page = 'douyinTrainSearchPage'
+
+    # 抖音养号首页帖子评论(浏览30-50帖子 随机时长 对其评论进行观看)
+    douyin_train_home_comment = 'douyinTrainHomeComment'
+
+    # 抖音紧急任务互动
+    douyin_urgent_interact = 'douyinComment'
+
+    # 抖音直播间互动
+    douyin_live_media_Interaction = 'douyinLiveMediaInteraction'
+
+
+# 任务key
+class task_key(Enum):
+    # 懂车帝互动key
+    DCD_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT = 'DCD_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT'
+
+    # 懂车帝养号
+    DCD_RANDOM_BROWSE = 'DCD_RANDOM_BROWSE'
+
+    # 抖音直播间
+    DY_COMING_BROADCASTING_ROOM = 'DY_COMING_BROADCASTING_ROOM'
+
+    # 抖音官媒任务
+    DY_OFFICIAL_MEDIA = 'DY_OFFICIAL_MEDIA'
+
+    # 特定内容评论
+    DY_SPECIFIC_TRANSFER_LIKE_COMMENT = 'DY_SPECIFIC_TRANSFER_LIKE_COMMENT'
+
+    # 抖音养号搜索关键词随机滑动
+    DY_SEARCH_RANDOM_BROWSE  = 'DY_SEARCH_RANDOM_BROWSE'
+
+    # 抖音养号随机滑动 点赞收藏
+    DY_RANDOM_BROWSE_LIKE_COLLECTION = 'DY_RANDOM_BROWSE_LIKE_COLLECTION'
+
+    # 抖音养号随机滑动 查看评论
+    DY_RANDOM_BROWSE_VIEW_COMMENTS = 'DY_RANDOM_BROWSE_VIEW_COMMENTS'
+
+    # 汽车之家官媒对应key
+    QCZJ_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT = 'QCZJ_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT'
+
+    # 易车官媒对应key
+    YC_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT = 'YC_SPECIFIC_SERARCH_TRANSFER_LIKE_COMMENT'
+
+    # 抖音评论点赞操作key
+    DY_COMMENT_LIKE_OPERATE = 'DY_COMMENT_LIKE_OPERATE'
+
+

+ 315 - 0
strategy/rpa_handler.py

@@ -0,0 +1,315 @@
+import json
+
+from cubic.cubic_simple_action import cubic_simple_action
+from dongchedi.simple_comment import simple_comment
+from douyin.douyin_officail_account_interact import douyin_random_watch_video
+from kuaishou.gifmaker_random_watch_video import gifmaker_random_watch_video
+from tools import loggerKit
+from strategy.rpa_enum import media_type, task_sub_type
+
+from douyin.douyin_random_search import douyin_random_search
+from douyin.simple_browser_video import douyin_spider as douyin_sign_browser_video
+from douyin.douyin_sign import douyin_spider as douyin_sign
+from douyin.douyin_watch_advise import douyin_spider as douyin_watch_advise
+from douyin.douyin_boxes_advise import douyin_spider as douyin_boxes_advise
+from douyin.douyin_user_operate import douyin_spider as douyin_user_demo
+from douyin.douyin_train_home_comment import douyin_spider as douyin_train_home_comment
+from douyin.douyin_interaction_urgent import douyin_spider as douyin_interact_urgent
+from douyin.douyin_live_comment import douyin_spider as douyin_live_comment
+from douyin.douyin_train_home_page import douyin_spider as douyin_train_home_page
+
+from dongchedi.random_browse import random_browse as dongchedi_content_interaction
+from dongchedi.dongchedi_sign import dongchedi_sign as dongchedi_sign
+
+from toutiao.search_double import search_task as search_task
+from toutiao.search_random import search_random_task as search_random_task
+from toutiao.watch_novel import watch_novel_task as watch_novel_task
+from toutiao.meal_allowance import meal_allowance_task as meal_allowance_task
+from xiaohongshu.xingin_random_watch_video import xingin_random_watch_video
+from yiche.yiche_direction_action import yiche_direction_action
+
+
+# 定义策略过程
+class rpa_processor_strategy:
+    def process_rpa(self, task):
+        pass
+
+
+# 抖音看视频领金币操作
+class douyin_browser_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_sign_browser_video(task.device_id, task.task_id, task.resource_name, task.media_channel,
+                                         task.request_data)
+
+
+# 抖音签到操作
+class douyin_sign_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_sign(task.device_id, task.execute_robot_name, task.task_id, task.resource_name,
+                           task.media_channel, task.request_data)
+
+
+# 抖音看广告操作
+class douyin_watch_advise_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_watch_advise(task.device_id, task.task_id, task.resource_name, task.media_channel,
+                                   task.request_data)
+
+
+# 抖音开宝箱+看广告操作
+class douyin_boxes_advise_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音demo脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_boxes_advise(task.device_id, task.execute_robot_name, task.task_id,
+                                   task.resource_name, task.media_channel, task.request_data)
+
+
+# 懂车帝随机养号
+class dongchedi_content_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝养号脚本操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return dongchedi_content_interaction(task.task_id, task.execute_robot_name, task.request_data)
+
+
+# 头条搜一搜赚钱任务
+class toutiao_search_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条搜一搜赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return search_task(task.task_id, task.request_data)
+
+
+# 头条随机搜赚钱任务
+class toutiao_search_random_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条搜一搜赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return search_random_task(task.task_id, task.request_data)
+
+
+# 头条看小说赚钱任务
+class toutiao_watch_novel_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条看小说赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return watch_novel_task(task.task_id, task.request_data)
+
+
+# 头条吃饭补贴任务
+class toutiao_meal_allwance_task_interaction_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行头条吃饭补贴脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return meal_allowance_task(task.task_id, task.request_data)
+
+
+# 抖音用户demo任务
+class douyin_user_demo_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音搜一搜赚钱脚本操作,请求信息:{2}", task.task_id,
+                       task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_user_demo(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+抖音极速版
+随机搜索
+"""
+
+
+class douyin_random_search_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行抖音随机搜索操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+
+        return douyin_random_search(task.task_id, task.resource_name, task.request_data)
+
+
+# 懂车帝签到操作
+class dongchedi_sign_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝签到操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return dongchedi_sign(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+懂车帝简单互动
+"""
+
+
+class dongchedi_simple_comment(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝简单互动操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return simple_comment(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+快手简单养号
+"""
+
+
+class kuaishou_random_watch_video_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行快手简单养号操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return gifmaker_random_watch_video(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+小红书简单养号
+"""
+
+
+class xiaohongshu_random_watch_video_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行快手简单养号操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return xingin_random_watch_video(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+易车官媒
+"""
+
+
+class yiche_direction_action_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行易车官媒操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return yiche_direction_action(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+汽车之家官媒
+"""
+
+
+class cubic_simple_action_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行汽车之家官媒操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return cubic_simple_action(task.task_id, task.resource_name, task.request_data)
+
+
+"""
+抖音常规版官媒
+"""
+
+
+class douyin_random_watch_video_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行快手简单养号操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_random_watch_video(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音养号首页(浏览30 - 50视频随机时长和点赞收藏)
+class douyin_train_home_page_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝签到操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_train_home_page(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音养号首页帖子评论(浏览30-50帖子 随机时长 对其评论进行观看)
+class douyin_train_home_comment_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝签到操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_train_home_comment(task.task_id, task.resource_name, task.request_data)
+
+
+# 抖音紧急互动
+class douyin_urgent_interact_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝签到操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_interact_urgent(task.task_id, task.resource_name, task.request_data)
+
+# 抖音紧急互动
+class douyin_live_operation(rpa_processor_strategy):
+    def process_rpa(self, task):
+        loggerKit.info("taskId:{0},设备号:{1},开始进行懂车帝签到操作,请求信息:{2}", task.task_id, task.device_id,
+                       json.dumps(task.to_dict(), ensure_ascii=False))
+        return douyin_live_comment(task.task_id, task.resource_name, task.request_data)
+
+
+# 定义上下文类
+class rpa_processor:
+    def __init__(self, strategy):
+        self.strategy = strategy
+
+    def set_strategy(self, strategy):
+        self.strategy = strategy
+
+    def process_rpa(self, task):
+        return self.strategy.process_rpa(task)
+
+
+class rpa_handler:
+    @staticmethod
+    def set_processor(task):
+        loggerKit.info(
+            'media_channel:{0}, action_type:{1},answer_type:{2}, task_type:{3} task_sub_type:{4} ,task_id:{5}',
+            task.media_channel, task.action_type,
+            task.answer_type, task.task_type, task.task_sub_type, task.task_id)
+
+        default_strategy = None  # 默认策略类
+
+        # 添加其他映射关系
+        strategy_mapping = {
+            (media_type.douyin.value, task_sub_type.douyin_sign_browser_video.value): douyin_browser_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_sign.value): douyin_sign_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_watch_advise.value): douyin_watch_advise_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_boxes_advise.value): douyin_boxes_advise_operation(),
+            (media_type.dongchedi.value,
+             task_sub_type.content_interaction.value): dongchedi_content_interaction_operation(),
+            (media_type.toutiao.value,
+             task_sub_type.toutiao_search_task.value): toutiao_search_task_interaction_operation(),
+            (media_type.toutiao.value,
+             task_sub_type.toutiao_search_random_task.value): toutiao_search_random_task_interaction_operation(),
+            (media_type.toutiao.value,
+             task_sub_type.toutiao_watch_novel_task.value): toutiao_watch_novel_task_interaction_operation(),
+            (media_type.toutiao.value,
+             task_sub_type.toutiao_meal_allowance_task.value): toutiao_meal_allwance_task_interaction_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_user_demo.value): douyin_user_demo_operation(),
+            (media_type.dongchedi.value, task_sub_type.dongchedi_sign.value): dongchedi_sign_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_random_search.value): douyin_random_search_operation(),
+            (media_type.douyin.value,
+             task_sub_type.douyin_train_home_comment.value): douyin_train_home_comment_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_train_home_page.value): douyin_train_home_page_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_urgent_interact.value): douyin_urgent_interact_operation(),
+            (media_type.dongchedi.value, task_sub_type.dongchedi_comment.value): dongchedi_simple_comment(),
+            (media_type.kuaishou.value, task_sub_type.kuaishou_train_home.value): kuaishou_random_watch_video_operation(),
+            (media_type.xiaohongshu.value,
+             task_sub_type.xiaohongshu.xiaohongshu_train_home.value): xiaohongshu_random_watch_video_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_office_media_operator.value): douyin_random_watch_video_operation(),
+            (media_type.yiche.value, task_sub_type.yi_che_office_media_operator.value): yiche_direction_action_operation(),
+            (media_type.auto_home.value, task_sub_type.auto_home_office_media_operator.value): cubic_simple_action_operation(),
+            (media_type.douyin.value, task_sub_type.douyin_live_media_Interaction.value): douyin_live_operation()
+        }
+
+        # 根据task.action_type和task.answer_type获取相应的策略类
+        strategy = strategy_mapping.get((task.media_channel, task.task_sub_type), default_strategy)
+
+        if strategy is None:
+            # 处理没有匹配到任何策略类的情况
+            raise ValueError("No strategy found for the given task")
+
+        we_strategy = rpa_processor(strategy)
+        return we_strategy

+ 0 - 0
task/__init__.py


Some files were not shown because too many files changed in this diff