| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 | import jsonimport timeimport uiautomator2 as u2import httpxfrom datetime import datetime, timedeltaimport subprocessfrom tools import loggerKitfrom difflib import SequenceMatcherclass 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 Falseif __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))
 |