Exemplo n.º 1
0
class CCB:
    package = 'com.chinamworld.main'
    main_activity = 'com.ccb.start.MainActivity'
    apk_version = '4.1.5'
    login_key_board = {
        '1': (0.061, 0.718),
        '2': (0.155, 0.718),
        '3': (0.26, 0.718),
        '4': (0.355, 0.718),
        '5': (0.455, 0.718),
        '6': (0.555, 0.718),
        '7': (0.655, 0.718),
        '8': (0.755, 0.718),
        '9': (0.855, 0.718),
        '0': (0.944, 0.718),
        'q': (0.061, 0.7984),
        'w': (0.155, 0.7984),
        'e': (0.26, 0.7984),
        'r': (0.355, 0.7984),
        't': (0.455, 0.7984),
        'y': (0.555, 0.7984),
        'u': (0.655, 0.7984),
        'i': (0.755, 0.7984),
        'o': (0.855, 0.7984),
        'p': (0.944, 0.7984),
        'a': (0.155, 0.90625),
        's': (0.26, 0.90625),
        'd': (0.355, 0.90625),
        'f': (0.455, 0.90625),
        'g': (0.555, 0.90625),
        'h': (0.655, 0.90625),
        'j': (0.755, 0.90625),
        'k': (0.855, 0.90625),
        'l': (0.944, 0.90625),
        'z': (0.213, 0.956),
        'x': (0.314, 0.956),
        'c': (0.407, 0.956),
        'v': (0.489, 0.956),
        'b': (0.6, 0.956),
        'n': (0.692, 0.956),
        'm': (0.801, 0.956),
        'back': (0.926, 0.956)
    }
    code_key_board = {
        '1': (0.167, 0.725),
        '2': (0.5, 0.725),
        '3': (0.833, 0.725),
        '4': (0.167, 0.804),
        '5': (0.5, 0.804),
        '6': (0.833, 0.804),
        '7': (0.167, 0.875),
        '8': (0.5, 0.875),
        '9': (0.833, 0.875),
        '0': (0.5, 0.978),
        '.': (0.167, 0.978)
    }

    def __init__(self, user, log_passwd, pay_passwd, k_passwd=''):
        self.user = user
        self.passwd = pay_passwd
        self.login_passwd = log_passwd
        self.driver = Driver()
        # 更新页面——关闭更新按钮
        self.__pattern_close = r'resource-id="com.chinamworld.main:id/close"\s+class="android.widget.ImageView".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_udpate_text = r'更新.+?clickable="true"[\s\S]*clickable="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 首页——余额按钮
        self.__pattern_main_remainds = r'text="首页"resource-id="com.chinamworld.main:id/totalMoney".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 首页——首页按钮
        self.__pattern_main_activity_main_page_btn = r'text="首页".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 首页——转账按钮
        self.__pattern_main_activity_transfer_btn = r'text="转账".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 待转账页面——转账按钮
        self.__pattern_transfer_btn = r'text="转账" resource-id="com.chinamworld.main:id/tv_function".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 登录页面——登录按钮
        self.__pattern_login = r'text="登录".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 登录页面——密码控件
        self.__pattern_pw_edit = r'resource-id="com.chinamworld.main:id/et_password".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——余额
        self.__pattern_remainds = r'text="人民币\s+活期储蓄\s+(.+?)" resource-id="com.chinamworld.main:id/tv_pay_info"'
        # 转账页面——收款人
        self.__pattern_reciver = r'text="请输入收款户名" resource-id="com.chinamworld.main:id/et_cash_name".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——收款账号
        self.__pattern_card = r'text="请输入收款账号或手机号" resource-id="com.chinamworld.main:id/et_collection_account".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——金额
        self.__pattern_money = r'text="请输入转账金额" resource-id="com.chinamworld.main:id/et_tran_amount".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 收款银行
        self.__pattern_bank_name = r'text="请选择收款银行" resource-id="com.chinamworld.main:id/tv_bank" class="android.widget.TextView".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 搜索
        self.__pattern_search = r'index="0" text="" resource-id="com.chinamworld.main:id/search_mid_layout" class="android.widget.RelativeLayout".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——下一步
        self.__pattern_next = r'text="下一步" resource-id="com.chinamworld.main:id/btn_right1".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面警告框——继续
        self.__pattern_continue = r'text="继续" resource-id="com.chinamworld.main:id/dlg_right_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——验证码控件
        self.__pattern_verify_code = r'resource-id="com.chinamworld.main:id/et_code".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——确认按钮
        self.__pattern_ok = r'text="确定" resource-id="com.chinamworld.main:id/btn_confirm".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——验证码序号
        self.__pattern_verify_seq = r'已向您手机号.+?发送序号为(\d+)的验证码'
        # 密码页面——密码控件
        self.__pattern_code = r'resource-id="com.chinamworld.main:id/et_code".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——图形验证码控件
        self.__pattern_pic_code = r'text="请输入右侧图片的字符" resource-id="com.chinamworld.main:id/native_graph_et".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——确认按钮
        self.__pattern_sure = r'text="确定" resource-id="com.chinamworld.main:id/btn_confirm".+?enabled="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——取消按钮
        self.__pattern_cancle = r'text="取消" resource-id="com.chinamworld.main:id/btn_cancel".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——图片区域
        self.__pattern_pic_rect = r'resource-id="com.chinamworld.main:id/native_graph_iv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 查询结果页面——查询按钮
        self.__pattern_check_result_btn = r'text="查询转账结果" resource-id="com.chinamworld.main:id/btn_right3".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 查询结果页面——取消按钮
        self.__pattern_check_result_cancle_btn = r'text="取消" resource-id="com.chinamworld.main:id/dlg_left_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 查询结果页面——确认按钮
        self.__pattern_check_result_sure_btn = r'text="确定" resource-id="com.chinamworld.main:id/dlg_right_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 异常界面
        self.__pattern_exception = r'text="关闭"\s+resource-id="com.chinamworld.main:id/dlg_right_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——关闭框框
        self.__pattern_close_verify_page = r'resource-id="com.chinamworld.main:id/iv_close".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        '''
        try:
            self.driver.power_on_screen()
            self.driver.unlock()
            self._enter_main_page()
        except BaseException as e:
            logger.error(f'{e}')
        '''

    def check_surplus(self):
        self.driver.power_on_screen()
        self.driver.unlock()
        logger.info('开始通过建设银行查询余额')
        # 回到首页
        try:
            xml = self._enter_main_page()

            # 从首页进入待转账页面
            xml = self.__enter_prepare_transfer(xml)

            # 从待转账页面进入转账页面
            xml = self.__enter_transfer(xml)

            # 转账页面
            return float(self.__transfer(xml, '', '', '', '', True)[0]), ''
        except BaseException as e:
            logger.error(f'查询余额失败,{e}')
            return '', str(e)

    def transfer_cash(self, reciver, card, money, taskid, bank_name):
        self.driver.power_on_screen()
        self.driver.unlock()
        self._remainds = ''
        logger.info(f'开始通过建设银行给{reciver}的卡{card}转账{money}元')
        try:
            xml = self._enter_main_page()

            # 从首页进入待转账页面
            xml = self.__enter_prepare_transfer(xml)

            # 从待转账页面进入转账页面
            xml = self.__enter_transfer(xml)

            # 转账页面
            self._remainds, xml = self.__transfer(xml, reciver, card,
                                                  str(money), bank_name)

            # 支付
            xml = self.__pay(self.passwd, xml)

            # 查询结果
            status, reason = self.check_result(xml)
            if status is True:
                self._remainds = round(float(self._remainds) - float(money), 2)
            return self._remainds, OK, reason
        except MyError as e:
            e = f'手机原因: {e}'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, PHONE_ERR, str(e)
        except NomoneyError as e:
            e = '卡余额不足'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, NO_MONEY, str(e)
        except UserError as e:
            e = f'用户原因: {e}'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, USER_ERR, str(e)
        except BaseException as e:
            e = f'其他原因: {e}'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, PHONE_ERR, str(e)

    def check_result(self, xml):
        start_time = time.time()
        while time.time() - start_time < 60:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.ccb.framework.security.base.successpage.CcbSuccessPageAct':
                check_result_btn = re.findall(self.__pattern_check_result_btn,
                                              xml)
                if len(check_result_btn) == 1:
                    self.__click(check_result_btn[0])
                elif '转账提交成功' in xml or re.search(
                        r'转账成功(.+?)" resource-id="com.chinamworld.main:id/tv_dlg_content"',
                        xml):
                    ret = '转账成功'
                    logger.info(ret)
                    return True, ret
                elif re.search(
                        r'具体原因:(.+?)" resource-id="com.chinamworld.main:id/tv_dlg_content"',
                        xml):
                    ret = re.findall(
                        r'具体原因:(.+?)" resource-id="com.chinamworld.main:id/tv_dlg_content"',
                        xml)[0]
                    logger.info(ret)
                    raise UserError(ret)
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('查询转账结果超时')

    # 支付页面
    def __pay(self, code, xml):
        # 密码输入
        def __input_code(xml, code):
            code_controler = re.findall(self.__pattern_code, xml)
            if len(code_controler) != 1:
                logger.error('未找到密码控件')
                return False
            # 输入密码
            self.__click(code_controler[0])
            time.sleep(0.5)
            for c in code:
                x = int(self.code_key_board[c][0] * self.driver.width())
                y = int(self.code_key_board[c][1] * self.driver.height())
                self.driver.click(x, y)

            # 验证码图片
            xml = self.driver.get_xml()
            pic_code = re.findall(self.__pattern_pic_code, xml)
            if len(pic_code) != 1:
                logger.error('未找到图形验证码控件')
                return False
            self.__click(pic_code[0])
            start_time = time.time()
            for i in range(10):
                pic_rect = re.findall(self.__pattern_pic_rect, xml)
                if len(pic_rect) != 1:
                    logger.error('未找到图片区域')
                    time.sleep(0.5)
                    continue
                pic_name = self.driver.screencap()
                ocr = OCR(pic_name)
                pic_rect = pic_rect[0]
                ocr.crop(int(pic_rect[0]), int(pic_rect[1]), int(pic_rect[2]),
                         int(pic_rect[3]))
                ocr.binaryzation(113)
                ocr.save(pic_name + '.verify.png')
                verify_code = ocr.get_text(pic_name + '.verify.png').lower()

                logger.info(f'第{i+1}次输入图形验证码: {verify_code}')
                for c in verify_code:
                    if c == ' ':
                        continue
                    x = int(self.login_key_board[c][0] * self.driver.width())
                    y = int(self.login_key_board[c][1] * self.driver.height())
                    self.driver.click(x, y)

                xml = self.driver.get_xml()
                sure = re.findall(self.__pattern_sure, xml)
                if len(sure) != 1:
                    logger.error('验证不正确')
                    for i in range(len(verify_code)):
                        x = int(self.login_key_board['back'][0] *
                                self.driver.width())
                        y = int(self.login_key_board['back'][1] *
                                self.driver.height())
                        self.driver.click(x, y)
                    self.__click(pic_rect)
                    time.sleep(0.5)
                else:
                    logger.info('验证码正确,点击确定')
                    self.__click(sure[0])
                    break
            return True

        start_time = time.time()
        while time.time() - start_time < 60:
            # 更新页面
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.ccb.framework.security.transecurityverify.TransSecurityVerifyDialogAct':
                # 校验界面
                if len(re.findall(self.__pattern_pic_code, xml)) == 1:
                    logger.info('已进入密码输入页面')
                    if __input_code(xml, code):
                        start_time = time.time()
                        continue

                verify_code = re.findall(self.__pattern_verify_code, xml)
                if len(verify_code) != 1:
                    logger.error('未找到验证码控件')
                    continue
                verify_seq = re.findall(self.__pattern_verify_seq, xml)
                if len(verify_seq) != 1:
                    logger.error('未找到验证码序列号')
                    continue
                ok = re.findall(self.__pattern_ok, xml)
                if len(ok) != 1:
                    logger.error('未找到确认支付按钮')
                    continue
                verify_seq = verify_seq[0]
                logger.info('验证码序列号为%s' % verify_seq)
                #self.__click(verify_code[0], True)
                # 输入短信验证码
                temp_start = time.time()
                while time.time() - temp_start < 120:
                    msg_list = MsgManger.getMsg('95533')
                    if len(msg_list) == 0:
                        time.sleep(0.5)
                        continue
                    msg = msg_list[0]
                    pattern = f'序号{verify_seq}的验证码(\d+),您向'
                    ret = re.findall(pattern, msg)
                    if len(ret) != 1:
                        logger.error(f'未能在短信 "{msg}"中匹配到 {pattern}')
                        time.sleep(1.5)
                        continue
                    if re.search(self.__pattern_verify_code, xml):
                        self.__click(
                            re.findall(self.__pattern_verify_code, xml)[0],
                            True)
                    logger.info(f'输入验证码{ret[0]}')
                    for c in ret[0]:
                        x = int(self.code_key_board[c][0] *
                                self.driver.width())
                        y = int(self.code_key_board[c][1] *
                                self.driver.height())
                        self.driver.click(x, y)
                    self.driver.back()
                    start_time = time.time()
                    break
                else:
                    raise MyError(f'未能收到序号为{verify_seq}的验证码')
                #time.sleep(30)
                logger.info('点击确认支付')
                self.__click(ok[0])
            elif cur_activity == 'com.ccb.framework.security.base.successpage.CcbSuccessPageAct':
                logger.info('转账已受理')
                return xml
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('支付超时')

    # 处理随机弹出页面:如更新,登录
    def __handle_random_page(self, activity, xml='', back=False):
        xml = self.driver.get_xml() if xml == '' else xml
        if activity == '':
            if len(re.findall(self.__pattern_udpate_text, xml)) == 1:
                ret = re.findall(self.__pattern_udpate_text, xml)
                logger.info('点击取消更新按钮')
                self.__click(ret[0])
                return True
            elif not back and len(re.findall(self.__pattern_continue,
                                             xml)) == 1:
                ret = re.findall(self.__pattern_continue, xml)
                logger.info('点击继续按钮')
                self.__click(ret[0])
                return True
        elif activity == 'com.ccb.start.view.startdialog.StartDialogActivity' or activity == 'com.ccb.transfer.transfer_home.view.TransferHomeAct':
            if re.search(self.__pattern_close, xml):
                logger.info('点击叉叉')
                self.__click(re.findall(self.__pattern_close, xml)[0])
                return True
            elif re.search(self.__pattern_udpate_text, xml):
                logger.info('取消更新按钮')
                self.__click(re.findall(self.__pattern_udpate_text, xml)[0])
                return True
        elif back is True and activity == 'com.ccb.transfer.smarttransfer.view.SmartTransferMainAct':
            if len(re.findall(self.__pattern_check_result_cancle_btn,
                              xml)) == 1:
                ret = re.findall(self.__pattern_check_result_cancle_btn, xml)
                logger.info(f'点击转账页面的取消按钮')
                self.__click(ret[0])
                return True
            if len(re.findall(self.__pattern_check_result_sure_btn, xml)) == 1:
                ret = re.findall(self.__pattern_check_result_sure_btn, xml)
                logger.info('点击转账页面的确定按钮')
                self.__click(ret[0])
                return True
        elif back and activity == 'com.ccb.framework.security.base.successpage.CcbSuccessPageAct':
            if len(re.findall(self.__pattern_check_result_sure_btn, xml)) == 1:
                ret = re.findall(self.__pattern_check_result_sure_btn, xml)
                logger.info('点击转账结果页面的确定按钮')
                self.__click(ret[0])
                return True
            elif len(re.findall(self.__pattern_check_result_cancle_btn,
                                xml)) == 1:
                ret = re.findall(self.__pattern_check_result_cancle_btn, xml)
                logger.info('点击转账结果页面的取消按钮')
                self.__click(ret[0])
                return True
        elif activity == 'com.ccb.start.MainActivity':
            if len(re.findall(self.__pattern_udpate_text, xml)) == 1:
                ret = re.findall(self.__pattern_udpate_text, xml)
                logger.info('点击取消更新按钮')
                self.__click(ret[0])
                return True
        elif activity == 'com.ccb.framework.security.login.internal.view.LoginActivity':
            if re.search(self.__pattern_close, xml):
                logger.info('点击叉叉')
                self.__click(re.findall(self.__pattern_close, xml)[0])
                return True
            elif re.search(self.__pattern_udpate_text, xml):
                logger.info('取消更新按钮')
                self.__click(re.findall(self.__pattern_udpate_text, xml)[0])
                return True
            logger.info('进入登录页面')
            ret = re.findall(self.__pattern_pw_edit, xml)
            if len(ret) != 1:
                logger.warning('未找到密码编辑框')
                return False
            logger.info('点击密码编辑框')
            self.__click(ret[0])
            time.sleep(0.5)  # 等待键盘弹出来

            # 匹配登录按钮
            ret = re.findall(self.__pattern_login, xml)
            if len(ret) != 1:
                logger.warning('未找到登录按钮')
                return False

            logger.info('输入密码中')
            for c in self.login_passwd:
                x = int(self.login_key_board[c][0] * self.driver.width())
                y = int(self.login_key_board[c][1] * self.driver.height())
                logger.info(f'{c}, {x}, {y}')
                self.driver.click(x, y)

            logger.info('点击登录按钮')
            self.__click(ret[0])
            time.sleep(2.5)
            return True
        elif re.search(self.__pattern_exception, xml):
            ret = re.findall(self.__pattern_exception, xml)[0]
            self.__click(ret)
            logger.info('点击关闭按钮')
            return True
        else:
            for error_text in ['收款方姓名与账户户名不一致', '交易金额低于总行允许的最低单笔限额']:
                if error_text in xml:
                    if back:
                        self.__click(
                            re.findall(self.__pattern_check_result_sure_btn,
                                       xml)[0])
                    else:
                        raise UserError(error_text)
                    return True
            else:
                logger.info(f'未知,activity: {activity}')
        return False

    # 首页进入待转账页面
    def __enter_prepare_transfer(self, xml):
        start_time = time.time()
        main_page_clicked = False
        while time.time() - start_time < 50:
            # 更新页面
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.ccb.start.MainActivity':
                '''
                # 匹配首页的首页按钮
                if not main_page_clicked:
                    ret = re.findall(self.__pattern_main_activity_main_page_btn, xml)
                    if len(ret) == 1:
                        logger.info('点击首页的首页按钮')
                        self.__click(ret[0])
                        main_page_clicked = True
                '''
                # 匹配的首页的转账按钮
                ret = re.findall(self.__pattern_main_activity_transfer_btn,
                                 xml)
                if len(ret) == 1:
                    logger.info('点击转账按钮')
                    self.__click(ret[0])
            elif cur_activity == 'com.ccb.transfer.transfer_home.view.TransferHomeAct':
                logger.info('已进入待转账页面')
                return xml
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('从首页进入转账页面超时')

    # 待转账页面进入正式转账页面
    def __enter_transfer(self, xml):
        start_time = time.time()
        while time.time() - start_time < 50:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.ccb.transfer.transfer_home.view.TransferHomeAct':
                ret = re.findall(self.__pattern_transfer_btn, xml)
                if len(ret) == 1:
                    logger.info('点击待转账页面的转账按钮')
                    self.__click(ret[0])
            elif cur_activity == 'com.ccb.transfer.smarttransfer.view.SmartTransferMainAct':
                logger.info('进入正式转账页面')
                return xml
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('从待转账页面进入转账页面超时')

    # 转账页面
    def __transfer(self,
                   xml,
                   name,
                   card,
                   money,
                   bank_name,
                   only4remainds=False):
        start_time = time.time()
        self._remainds = ''
        while time.time() - start_time < 80:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.ccb.transfer.smarttransfer.view.SmartTransferMainAct':
                if re.search(self.__pattern_continue, xml):
                    ret = re.findall(self.__pattern_continue, xml)[0]
                    logger.info('点击继续')
                    self.__click(ret)
                    continue
                if re.search(self.__pattern_check_result_sure_btn, xml):
                    ret = re.findall(self.__pattern_continue, xml)[0]
                    logger.info('点击确定')
                    self.__click(ret)
                    continue
                '''
                ret_remainds = xml.find('活期储蓄')
                if ret_remainds == -1:
                    xml = self.driver.get_xml()
                    ret_remainds = xml.find('活期储蓄')
                    if ret_remainds == -1:
                        logger.error(f'未找到余额, {cur_activity}, {xml}')
                        continue
                ret_remainds = xml[ret_remainds + len('活期储蓄') : ]
                ret_remainds = [float(ret_remainds[: ret_remainds.find('"')])]
                '''
                # 正则表达式不稳定
                ret_remainds = re.findall(self.__pattern_remainds, xml)
                if len(ret_remainds) != 1:
                    logger.error(f'未找到余额, {ret_remainds}')
                    continue

                if only4remainds:
                    logger.info(f'余额:{ret_remainds[0]}')
                    return re.sub(r',', '', ret_remainds[0]), xml

                ret_reciver = re.findall(self.__pattern_reciver, xml)
                if len(ret_reciver) != 1:
                    logger.error('未找到收款人控件')
                    continue
                ret_card = re.findall(self.__pattern_card, xml)
                if len(ret_card) != 1:
                    logger.error('未找到卡号控件')
                    continue
                ret_money = re.findall(self.__pattern_money, xml)
                if len(ret_money) != 1:
                    logger.error('未找到转账金额控件')
                    continue
                ret_next = re.findall(self.__pattern_next, xml)
                if len(ret_next) != 1:
                    logger.error('未找到下一步控件')
                    continue

                self._remainds = re.sub(r',', '', ret_remainds[0])
                logger.info(f'余额:{self._remainds}')
                if float(self._remainds) < float(money):
                    raise NomoneyError(f'余额不足{money}元,当前余额{self._remainds}元')

                logger.info('输入收款人 %s' % name)
                self.__click(ret_reciver[0])
                self.driver.input_text(name)

                logger.info('输入卡号 %s' % card)
                self.__click(ret_card[0])
                for c in card:
                    x = int(self.code_key_board[c][0] * self.driver.width())
                    y = int(self.code_key_board[c][1] * self.driver.height())
                    self.driver.click(x, y)
                self.driver.back()
                time.sleep(2)
                temp_start = time.time()
                while time.time() - temp_start < 30:
                    ret_money = re.findall(self.__pattern_money,
                                           self.driver.get_xml())
                    if len(ret_money) != 1:
                        time.sleep(2)
                        continue
                    logger.info('输入金额 %s' % money)
                    self.__click(ret_money[0])
                    for c in money:
                        x = int(self.code_key_board[c][0] *
                                self.driver.width())
                        y = int(self.code_key_board[c][1] *
                                self.driver.height())
                        self.driver.click(x, y)
                    self.driver.back()
                    time.sleep(0.5)
                    xml = self.driver.get_xml()
                    if re.search(self.__pattern_bank_name, xml):
                        raise UserError(f'未选择银行')
                    self.driver.swip(0.5, 0.7, 0.5, 0.3)
                    logger.info('点击下一步')
                    self.__click(ret_next[0])
                    start_time = time.time()
                    break
                else:
                    raise MyError(f'可能网络状况不佳,导致一直找不到输入金额的控件')
            elif cur_activity == 'com.ccb.framework.security.transecurityverify.TransSecurityVerifyDialogAct':
                logger.info('已进入付款页面')
                return self._remainds, xml
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('转账页面操作超时')

    # 进入首页
    def _enter_main_page(self):
        #self.driver.stop_app(CCB.package)
        package = self.driver.get_cur_packge()
        # 启动app
        if package != CCB.package:
            self.driver.start_app(CCB.package, CCB.main_activity)
            cur_time = time.time()
            while time.time() - cur_time <= 30:
                if self.driver.is_app_started(CCB.package):
                    logger.info('启动app成功')
                    break
                time.sleep(1)
            for i in range(3):
                if self.driver.is_app_started(CCB.package):
                    break
                time.sleep(0.5)
            else:
                raise MyError('启动APP失败')

        # 进入首页
        start_time = time.time()
        while time.time() - start_time < 30:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml, True):  # 更新页面
                start_time = time.time()
                continue
            elif cur_activity != 'com.ccb.start.MainActivity':
                if cur_activity == 'com.ccb.framework.security.transecurityverify.TransSecurityVerifyDialogAct':
                    ret = re.findall(self.__pattern_cancle, xml)
                    if len(ret) == 1:
                        self.__click(ret[0])

                    ret = re.findall(self.__pattern_close_verify_page, xml)
                    logger.info(f'{ret}')
                    if len(ret) == 1:
                        self.__click(ret[0])
                elif cur_activity == 'com.ccb.framework.security.base.successpage.CcbSuccessPageAct':
                    ret = re.findall(self.__pattern_cancle, xml)
                    if len(ret) == 1:
                        self.__click(ret[0])
                logger.info('点击返回键')
                self.driver.back()
            else:
                logger.info('已进入首页')
                return xml
            time.sleep(0.5)
        raise MyError('回到APP首页超时失败')

    def __click(self, pos, double=False):
        click_x = (int(pos[0]) + int(pos[2])) // 2
        click_y = (int(pos[1]) + int(pos[3])) // 2
        self.driver.click(click_x, click_y, double)
Exemplo n.º 2
0
                    logger.info(f'{ret}')
                    if len(ret) == 1:
                        self.__click(ret[0])
                elif cur_activity == 'com.ccb.framework.security.base.successpage.CcbSuccessPageAct':
                    ret = re.findall(self.__pattern_cancle, xml)
                    if len(ret) == 1:
                        self.__click(ret[0])
                logger.info('点击返回键')
                self.driver.back()
            else:
                logger.info('已进入首页')
                return xml
            time.sleep(0.5)
        raise MyError('回到APP首页超时失败')

    def __click(self, pos, double=False):
        click_x = (int(pos[0]) + int(pos[2])) // 2
        click_y = (int(pos[1]) + int(pos[3])) // 2
        self.driver.click(click_x, click_y, double)
        #logger.debug(f'clicked {click_x} {click_y}')


if __name__ == '__main__':
    driver = Driver()
    serial = '1234567890qwertyuiopasdfghjklzxcvbnm'
    for c in serial:
        x = int(CCB.login_key_board[c][0] * driver.width())
        y = int(CCB.login_key_board[c][1] * driver.height())
        print(c, x, y)
        driver.click(x, y)
Exemplo n.º 3
0
class PinAn:
    package = 'com.pingan.paces.ccms'
    main_activity = 'com.pingan.pocketbank.splash.PALoadingActivity'
    apk_version = '4.1.5'
    verify_edit = (0.3542, 0.5125)
    login_key_board_character = {
        'q': (0.069, 0.7319),
        'w': (0.155, 0.7319),
        'e': (0.26, 0.7319),
        'r': (0.355, 0.7319),
        't': (0.455, 0.7319),
        'y': (0.555, 0.7319),
        'u': (0.655, 0.7319),
        'i': (0.755, 0.7319),
        'o': (0.855, 0.7319),
        'p': (0.944, 0.7319),
        'a': (0.111, 0.77579),
        's': (0.2056, 0.77579),
        'd': (0.3018, 0.77579),
        'f': (0.4, 0.77579),
        'g': (0.4944, 0.77579),
        'h': (0.599, 0.77579),
        'j': (0.6963, 0.77579),
        'k': (0.795, 0.77579),
        'l': (0.8889, 0.77579),
        'z': (0.2056, 0.86336),
        'x': (0.302, 0.86336),
        'c': (0.399, 0.86336),
        'v': (0.497, 0.86336),
        'b': (0.598, 0.86336),
        'n': (0.702, 0.86336),
        'm': (0.797, 0.86336)
    }

    char2digital = (0.109, 0.9532)
    finish = (0.9139, 0.6566)
    login_key_board_digital = {
        '1': (0.1556, 0.6838),
        '2': (0.513, 0.6838),
        '3': (0.8278, 0.6838),
        '4': (0.1556, 0.7741),
        '5': (0.513, 0.7741),
        '6': (0.8278, 0.7741),
        '7': (0.1556, 0.8645),
        '8': (0.513, 0.8645),
        '9': (0.8278, 0.8645),
        '0': (0.513, 0.956),
        '.': (0.1556, 0.956)
    }

    def __init__(self, user, log_passwd, pay_passwd, k_passwd=''):
        self.user = user
        self.passwd = pay_passwd
        self.login_passwd = log_passwd
        self.driver = Driver()
        # 首页——首页按钮
        self.__pattern_main_activity_main_page_btn = r'text="首页".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 更新页面——关闭更新按钮
        self.__pattern_cancle = r'text="取消" resource-id="com.pingan.paces.ccms:id/btn_left".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 首页的编辑框
        self.__pattern_search = r'resource-id="com.pingan.paces.ccms:id/base_header_view_middle_search_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]'
        # 编辑框的清空按钮
        self.__pattern_clear = r'text=""\s+resource-id="com.pingan.paces.ccms:id/launcher_header_delete_img"\s+class="android.widget.ImageView".+?clickable="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 编辑框的转账选项
        #self.__pattern_tansfer_btn = r'text="转账"\s+resource-id=""\s+class="android.view.View".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'   # 小米
        self.__pattern_tansfer_btn = r'resource-id=""\s+class="android.view.View"\s+package="com.pingan.paces.ccms"\s+content-desc="转账"\s+.+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'  # nexus 5
        # 待转账页面——转账
        #self.__pattern_tansfer = r'text="银行账号转账".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_tansfer = r'content-desc="银行账号转账".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——余额
        #self.__pattern_remainds = r'text="可用余额:([\d\.]+)元"'
        self.__pattern_remainds = r'content-desc="可用余额:([\d\.\,]+)元"'
        # 转账页面——继续转账按钮
        #self.__pattern_continue = r'text="继续转账\s*".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_continue = r'content-desc="继续转账\s*".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——确认按钮
        self.__pattern_sure = r'text="确认".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——收款人
        self.__pattern_reciver = r'resource-id="nameInput".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——卡号
        self.__pattern_card = r'resource-id="inputCardNum".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——金额
        #self.__pattern_money = r'text="免手续费".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_money = r'content-desc="免手续费".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——下一步
        #self.__pattern_next = r'text="下一步".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_next = r'content-desc="下一步".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 登录页面的登录密码编辑框
        #self.__pattern_login_code = r'text="登录密码".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'   # 6A
        self.__pattern_login_code = r'content-desc="登录密码".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'  # nexus 5
        # 登录页面的登录按钮
        #self.__pattern_login = r'text="登录"\s+resource-id="submit"\s+class="android.widget.Button".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'   # 6A
        self.__pattern_login = r'class="android.widget.Button"\s+package="com.pingan.paces.ccms"\s+content-desc="登录".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'  # nexus 5

        # 收不到短信验证码提示
        self.__pattern_sms_prompt = r'NAF="true" index="1" text="" resource-id="" class="android.view.View" package="com.pingan.paces.ccms".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 取消联系人
        self.__pattern_cancel_contact = r'resource-id="android:id/button1".+?content-desc="取消".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'

    def check_surplus(self):
        self.driver.power_on_screen()
        self.driver.unlock()
        logger.info('开始通过平安银行查询余额')
        # 回到首页
        try:
            self._enter_main_page()

            # 从首页进入待转账页面
            self.__enter_prepare_transfer()

            # 转账页面
            return float(self.__enter_transfer('', '', '', True)), ''
        except BaseException as e:
            logger.error(f'查询余额失败,{e}')
            logger.error(f"{traceback.print_exc()}")
            return '', str(e)

    def transfer_cash(self, reciver, card, money, taskid, bank_name):
        self.driver.power_on_screen()
        self.driver.unlock()
        self._remainds = ''
        logger.info(f'开始通过平安银行给{reciver}的卡{card}转账{money}元')
        try:
            # 回到首页
            self._enter_main_page()

            # 从首页进入待转账页面
            self.__enter_prepare_transfer()

            # 转账页面
            self.__enter_transfer(reciver, card, str(money))

            # 支付
            status, reason = self.__pay(self.passwd)
            if status is True:
                self._remainds = round(float(self._remainds) - float(money), 2)
            return self._remainds, OK, reason
        except MyError as e:
            e = f'手机原因: {e}'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, PHONE_ERR, str(e)
        except NomoneyError as e:
            e = '卡余额不足'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, NO_MONEY, str(e)
        except UserError as e:
            e = f'用户原因: {e}'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, USER_ERR, str(e)
        except BaseException as e:
            e = f'其他原因: {e}'
            logger.error(e)
            logger.error(traceback.format_exc())
            return self._remainds, PHONE_ERR, str(e)

    def _enter_main_page(self):
        #self.driver.stop_app(PinAn.package)
        package = self.driver.get_cur_packge()
        logger.info(f"当前包名:{package}, 平安包名:{PinAn.package}")
        # 启动app
        if package != PinAn.package:
            self.driver.start_app(PinAn.package, PinAn.main_activity)
            cur_time = time.time()
            while time.time() - cur_time <= 30:
                if self.driver.is_app_started(PinAn.package):
                    logger.info('启动app成功')
                    break
                time.sleep(1)
            for i in range(3):
                if self.driver.is_app_started(PinAn.package):
                    break
                time.sleep(0.5)
            else:
                raise MyError('启动APP失败')
        # 进入首页
        start_time = time.time()
        while time.time() - start_time < 60:
            cur_activity = self.driver.get_cur_activity()
            if self.__handle_random_page(cur_activity):  # 更新页面
                start_time = time.time()
                continue
            elif cur_activity != 'com.pingan.launcher.activity.LauncherActivity':
                self.driver.back()
            else:
                logger.info('已进入首页')
                return
        raise MyError('回到APP首页超时失败')

    def __enter_prepare_transfer(self):
        start_time = time.time()
        while time.time() - start_time < 50:
            xml = self.driver.get_xml()
            # 更新页面
            cur_activity = self.driver.get_cur_activity()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.pingan.launcher.activity.LauncherActivity':
                # 匹配首页的首页按钮
                ret = re.findall(self.__pattern_search, xml)
                if len(ret) == 1:
                    logger.info('点击首页的搜索框')
                    self.__click(ret[0])
                else:
                    logger.warning(f'为在首页匹配到搜索框')
            elif cur_activity == 'com.pingan.core.base.VoiceSearchActivity':
                logger.info(f'进入搜索框')
                if re.search(self.__pattern_clear, xml):
                    logger.info(f'点击搜索框的清空图标')
                    self.__click(re.findall(self.__pattern_clear, xml)[0])
                self.driver.input_text('转账')
                # 匹配的首页的转账按钮
                time.sleep(1.5)
                xml = self.driver.get_xml()
                ret = re.findall(self.__pattern_tansfer_btn, xml)
                if len(ret) == 1:
                    logger.info(f'点击搜索框页面的转账按钮')
                    self.__click(ret[0])
                    start_time = time.time()
            elif cur_activity == 'com.pingan.core.base.PocketWebViewActivity':
                logger.info('已进入待转账页面')
                return
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('从首页进入转账页面超时')

    def __enter_transfer(self, name, card, money, only4remainds=False):
        start_time = time.time()
        self._remainds = ''
        while time.time() - start_time < 70:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            logger.info(f'---{cur_activity}')
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif '取款密码' in xml:
                logger.info('进入支付页面')
                return
            elif re.search(self.__pattern_sure, xml):
                logger.info('点击确定')
                self.__click(re.findall(self.__pattern_sure, xml)[0])
                start_time = time.time()
            elif re.search(self.__pattern_continue, xml):
                logger.info('点击继续转账')
                ret = re.findall(self.__pattern_continue, xml)[0]
                self.__click(ret)
                start_time = time.time()
            elif '可用余额' in xml:
                logger.info('进入正式转账页面')
                ret_remainds = re.findall(self.__pattern_remainds, xml)
                if len(ret_remainds) != 1:
                    logger.error(f'未找到余额')
                    continue
                if only4remainds:
                    logger.info(f'余额:{ret_remainds[0]}')
                    return re.sub(',', '', ret_remainds[0])

                ret_reciver = re.findall(self.__pattern_reciver, xml)
                if len(ret_reciver) != 1:
                    logger.error('未找到收款人控件')
                    continue
                ret_card = re.findall(self.__pattern_card, xml)
                if len(ret_card) != 1:
                    logger.error('未找到卡号控件')
                    continue
                ret_next = re.findall(self.__pattern_next, xml)
                if len(ret_next) != 1:
                    logger.error('未找到下一步控件')
                    continue

                self._remainds = re.sub(',', '', ret_remainds[0])
                logger.info(f'余额:{self._remainds}')
                if float(self._remainds) < float(money):
                    raise NomoneyError(f'余额不足{money}元,当前余额{self._remainds}元')

                logger.info('输入卡号 %s' % card)
                self.__click(ret_card[0])
                self.driver.input_text(card)
                xml = self.driver.get_xml()
                if '请选择' in xml:
                    raise UserError(f'卡号不存在')

                logger.info('输入收款人 %s' % name)
                ret_reciver = re.findall(self.__pattern_reciver, xml)
                self.__click(ret_reciver[0])
                self.driver.input_text(name)

                xml = self.driver.get_xml()
                ret_money = re.findall(self.__pattern_money, xml)
                if len(ret_money) != 1:
                    logger.error('未找到转账金额控件')
                    raise MyError('未找到转账金额控件')
                logger.info('输入金额 %s' % money)
                self.__click(ret_money[0])
                time.sleep(1)
                for c in money:
                    x = int(self.login_key_board_digital[c][0] *
                            self.driver.width())
                    y = int(self.login_key_board_digital[c][1] *
                            self.driver.height())
                    self.driver.click(x, y)
                self.driver.click(self.finish[0] * self.driver.width(),
                                  self.finish[1] * self.driver.height())
                time.sleep(2)
                xml = self.driver.get_xml()
                if re.search(self.__pattern_cancel_contact, xml):
                    logger.info(f'点击取消联系人按钮')
                    self.__click(
                        re.findall(self.__pattern_cancel_contact, xml)[0])
                    time.sleep(0.5)
                self.driver.swip(0.5, 0.7, 0.5, 0.3)
                ret_next = re.findall(self.__pattern_next,
                                      self.driver.get_xml())
                if len(ret_next) != 1:
                    logger.error('未找到下一步控件')
                    raise MyError('未找到下一步控件')
                logger.info('点击下一步')
                self.__click(ret_next[0])
                start_time = time.time()
            elif cur_activity == 'com.pingan.core.base.PocketWebViewActivity':
                ret = re.findall(self.__pattern_tansfer, xml)
                if len(ret) == 1:
                    logger.info('点击待转账页面的转账按钮')
                    self.__click(ret[0])
            else:
                logger.warning(f'未知 activity {cur_activity}')
        raise MyError('从待转账页面进入转账页面超时')

    def __pay(self, code):
        start_time = time.time()
        while time.time() - start_time < 70:
            # 更新页面
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.pingan.core.base.PocketWebViewActivity':
                if '"收不到验证码"' in xml and re.search(self.__pattern_sms_prompt,
                                                   xml):
                    logger.info(f'取消收不到短信提醒框')
                    self.__click(re.findall(self.__pattern_sms_prompt, xml)[0])
                elif '的取款密码' in xml:
                    logger.info(f'输入取款密码')
                    for c in code:
                        x = int(self.login_key_board_digital[c][0] *
                                self.driver.width())
                        y = int(self.login_key_board_digital[c][1] *
                                self.driver.height())
                        self.driver.click(x, y)
                    msg_start_time = time.time()
                    self.driver.click(self.finish[0] * self.driver.width(),
                                      self.finish[1] * self.driver.height())
                    time.sleep(1)
                elif '验证码' in xml and '手机号' in xml and '查收' in xml:
                    logger.info('进入验证码输入界面')
                    temp_start = time.time()
                    while time.time() - temp_start < 120:
                        msg_list = MsgManger.getMsg('95511',
                                                    int(msg_start_time))
                        if len(msg_list) == 0:
                            time.sleep(1)
                            continue
                        msg = msg_list[0]
                        pattern = f'动态码(\d+),收款人'
                        ret = re.findall(pattern, msg)
                        if len(ret) != 1:
                            logger.error(f'未能在短信{msg}中匹配到 {pattern}')
                            time.sleep(0.5)
                            continue
                        logger.info('双击验证码编辑框')
                        self.__click([107, 837, 658, 1002], True)
                        logger.info(f'输入验证码{ret[0]}')
                        for c in ret[0]:
                            x = int(self.login_key_board_digital[c][0] *
                                    self.driver.width())
                            y = int(self.login_key_board_digital[c][1] *
                                    self.driver.height())
                            self.driver.click(x, y)
                        start_time = msg_start_time = time.time()
                        break
                    else:
                        raise MyError('120秒内未能收到短信验证码')
                elif '转账' in xml and '完成' in xml:
                    logger.info('进入转账查询结果界面')
                    if '转账提交成功' in xml:
                        '''
                        logger.info(f'转账成功,等待支付结果短信')
                        temp_start = time.time()
                        while time.time() - temp_start < 40:
                            msg_list = MsgManger.getMsg('106927995511', int(msg_start_time))
                            if len(msg_list) == 0:
                                time.sleep(0.3)
                                continue
                            ret = msg_list[0]
                            logger.info(ret)
                            return True, ret
                        '''
                        return True, ''
                    elif re.search(
                            r'content-desc="转账失败"[\s\S]+?<node index="2".+?content-desc="(.+?)"',
                            xml):
                        ret = re.findall(
                            r'content-desc="转账失败"[\s\S]+?<node index="2".+?content-desc="(.+?)"',
                            xml)[0]
                        logger.info(ret)
                        raise UserError(ret)
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('支付超时')

    def __handle_random_page(self, cur_activity, xml=''):
        if cur_activity == 'com.pingan.launcher.activity.LauncherActivity' or cur_activity == PinAn.main_activity:
            xml = self.driver.get_xml() if xml == '' else xml
            if re.search(self.__pattern_cancle, xml):
                logger.info(f'点击取消按钮')
                self.__click(re.findall(self.__pattern_cancle, xml)[0])
                return True
        elif cur_activity == 'com.pingan.core.base.PocketWebViewActivity':
            xml = self.driver.get_xml() if xml == '' else xml
            if re.search(self.__pattern_login_code, xml):
                logger.info(f'进入登录页面')
                self.__click(
                    re.findall(self.__pattern_login_code, xml)[0], xml)
                logger.info(f'输入登录密码')
                cur = CHARACTER
                keyboard = self.login_key_board_character
                for c in self.login_passwd:
                    if c.isdigit() and cur == CHARACTER:
                        self.driver.click(
                            self.char2digital[0] * self.driver.width(),
                            self.char2digital[1] * self.driver.height())
                        time.sleep(0.5)
                        cur = DIGITAL
                        keyboard = self.login_key_board_digital
                    elif c.isalpha() and cur == DIGITAL:
                        self.driver.click(
                            self.char2digital[0] * self.driver.width(),
                            self.char2digital[1] * self.driver.height())
                        time.sleep(0.5)
                        cur = CHARACTER
                        keyboard = self.login_key_board_character
                    x = int(keyboard[c][0] * self.driver.width())
                    y = int(keyboard[c][1] * self.driver.height())
                    #self.driver.swip_pos(x, y, x+1, y+1, 1)
                    self.driver.click(x, y)
                self.driver.click(self.finish[0] * self.driver.width(),
                                  self.finish[1] * self.driver.height())
                time.sleep(0.3)
                logger.info('点击登录按钮')
                self.__click(
                    re.findall(self.__pattern_login, self.driver.get_xml())[0])
                return True
        return False

    def __click(self, pos, double=False):
        click_x = (int(pos[0]) + int(pos[2])) // 2
        click_y = (int(pos[1]) + int(pos[3])) // 2
        self.driver.click(click_x, click_y, double)
Exemplo n.º 4
0
class ABC:
    package = 'com.android.bankabc'
    main_activity = 'com.android.bankabc.homepage.HomeActivity'  # 'com.android.bankabc.SplashActivity'
    apk_version = '4.1.1'

    login_digit_keyboard_templateSign = '78ca7d7b1c20f85bedacf65f61ddbc99'  # 登录键盘数字键盘的模板码
    login_char_keyboard_templateSign = '703be84400e3c37ae2533a9192c66e74'  # 登录键盘字符键盘的模板码
    login_digit2char = (0.6111, 0.942)
    login_char2digit = (0.1296, 0.942)
    login_btn = (0.8889, 0.942)
    login_digit_keyboard = {
        'key1': (0.1297, 0.6577),
        'key2': (0.3722, 0.6577),
        'key3': (0.6074, 0.6577),
        'key4': (0.1297, 0.7609),
        'key5': (0.3722, 0.7609),
        'key6': (0.6074, 0.7609),
        'key7': (0.1297, 0.8528),
        'key8': (0.3722, 0.8528),
        'key9': (0.6074, 0.8528),
        'key10': (0.3722, 0.9651)
    }
    login_char_keyboard = {
        'key01': (0.0463, 0.6689),
        'key02': (0.1509, 0.6689),
        'key03': (0.25, 0.6689),
        'key04': (0.3537, 0.6689),
        'key05': (0.4444, 0.6689),
        'key06': (0.5481, 0.6689),
        'key07': (0.6481, 0.6689),
        'key08': (0.7509, 0.6689),
        'key09': (0.8528, 0.6689),
        'key10': (0.9453, 0.6689),
        'key11': (0.1019, 0.7816),
        'key12': (0.2009, 0.7816),
        'key13': (0.2991, 0.7816),
        'key14': (0.3981, 0.7816),
        'key15': (0.4981, 0.7816),
        'key16': (0.6065, 0.7816),
        'key17': (0.6991, 0.7816),
        'key18': (0.8028, 0.7816),
        'key19': (0.8972, 0.7816),
        'key20': (0.2009, 0.8811),
        'key21': (0.2991, 0.8811),
        'key22': (0.3981, 0.8811),
        'key23': (0.4981, 0.8811),
        'key24': (0.6065, 0.8811),
        'key25': (0.6991, 0.8811),
        'key26': (0.8028, 0.8811)
    }

    white_pay_digit_keyboard_templateSign = 'cb9df66d98ee5a08702866d85cf66c99'  # 白色款支付键盘数字键盘的模板码
    white_pay_char_keyboard_templateSign = '1a889cefba4710dc980c27e6870ad3c3'  # 白色款支付键盘字符键盘的模板码
    white_pay_digit2char = (0.8740, 0.98)
    pay_btn = (0.6731, 0.5678)
    white_pay_digit_keyboard = {
        'key1': (0.1222, 0.8067),
        'key2': (0.3722, 0.8067),
        'key3': (0.6167, 0.8067),
        'key4': (0.8740, 0.8067),
        'key5': (0.1222, 0.8942),
        'key6': (0.3722, 0.8942),
        'key7': (0.6167, 0.8942),
        'key8': (0.8740, 0.8528),
        'key9': (0.1222, 0.98),
        'key10': (0.3722, 0.98)
    }
    white_pay_char_keyboard = {
        'key01': (0.0491, 0.8062),
        'key02': (0.1454, 0.8062),
        'key03': (0.2417, 0.8062),
        'key04': (0.3463, 0.8062),
        'key05': (0.4519, 0.8062),
        'key06': (0.5417, 0.8062),
        'key07': (0.64, 0.8062),
        'key08': (0.7472, 0.8062),
        'key09': (0.8472, 0.8062),
        'key10': (0.9426, 0.8062),
        'key11': (0.0491, 0.8937),
        'key12': (0.1454, 0.8937),
        'key13': (0.2417, 0.8937),
        'key14': (0.3463, 0.8937),
        'key15': (0.4519, 0.8937),
        'key16': (0.5417, 0.8937),
        'key17': (0.64, 0.8937),
        'key18': (0.7472, 0.8937),
        'key19': (0.8472, 0.8937),
        'key20': (0.9426, 0.8937),
        'key21': (0.1454, 0.9778),
        'key22': (0.2417, 0.9778),
        'key23': (0.3463, 0.9778),
        'key24': (0.4519, 0.9778),
        'key25': (0.5417, 0.9778),
        'key26': (0.64, 0.9778)
    }

    black_pay_digit_keyboard_templateSign = '854df9b3c1c0203d954eb0470cb59f01'  # 黑色款支付键盘数字键盘的模板码
    black_pay_char_keyboard_templateSign = ''  # 黑色款支付键盘字符键盘的模板码
    black_pay_digit2char = (0.8935, 0.9532)
    black_pay_digit_keyboard = {
        'key1': (0.1296, 0.7971),
        'key2': (0.3704, 0.7971),
        'key3': (0.6292, 0.7971),
        'key4': (0.8704, 0.7971),
        'key5': (0.1296, 0.8751),
        'key6': (0.3704, 0.8751),
        'key7': (0.6292, 0.8751),
        'key8': (0.8704, 0.8751),
        'key9': (0.1296, 0.9532),
        'key10': (0.3704, 0.9532)
    }
    black_pay_char_keyboard = {}

    url = 'http://atf.wuwotech.com/api/equipment/kcard'

    def __init__(self, user, log_passwd, pay_passwd, k_passwd, k_color):
        self.k_passwd = k_passwd
        self.user = user
        self.passwd = pay_passwd
        self.login_passwd = log_passwd
        self.kcolor = k_color
        self.driver = Driver()
        # 更新页面——关闭更新按钮
        self.__pattern_close = r'resource-id="com.chinamworld.main:id/close"\s+class="android.widget.ImageView".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_udpate_text = r'更新.+?clickable="true"[\s\S]*clickable="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 首页——余额按钮
        self.__pattern_main_remainds = r'text="首页"resource-id="com.chinamworld.main:id/totalMoney".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 确认按钮
        self.__pattern_sure = r'text="\s*确定\s*".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        self.__pattern_next_pay = r'text="\s*下一步\s*".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 完成按钮
        self.__pattern_complish = r'text="\s*完成\s*".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 返回按钮
        self.__pattern_back = r'text="\s*返回\s*".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 首页——转账按钮
        self.__pattern_main_activity_transfer_btn = r'text="转账".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 待转账页面——转账按钮
        self.__pattern_transfer_btn = r'text="转账".+?class="android.widget.TextView".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 登录页面——登录按钮
        self.__pattern_login = r'text="登录".+?class="android.widget.Button".+?clickable="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 登录页面——密码控件
        self.__pattern_pw_edit = r'text="请输入登录密码".+?class="android.widget.EditText".+?clickable="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 登录页面——返回控件
        self.__pattern_back_edit = r'NAF="true".+?text="".+?class="android.widget.Button".+?clickable="true".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——余额
        self.__pattern_remainds = r'text="可用金额\s*(.+?)\s*元" resource-id="" class="android.widget.TextView"'
        # 转账页面——收款人
        self.__pattern_reciver = r'text="请输入或选择收款方" resource-id="" class="android.widget.EditText".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——收款账号
        self.__pattern_card = r'text="请输入账号或手机号" resource-id="" class="android.widget.EditText".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——金额
        self.__pattern_money = r'text="请输入转账金额" resource-id="" class="android.widget.EditText".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 转账页面——下一步
        self.__pattern_next = r'text="下一步" resource-id="" class="android.widget.Button".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 收款银行
        self.__pattern_bank_name = r'text="请选择银行" resource-id="" class="android.widget.TextView".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 搜索
        self.__pattern_search = r'text="搜索" resource-id="com.android.bankabc:id/edt_search" class="android.widget.EditText".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'

        # 转账页面警告框——继续
        self.__pattern_continue = r'text="继续" resource-id="com.chinamworld.main:id/dlg_right_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——验证码控件
        self.__pattern_verify_code = r'resource-id="com.chinamworld.main:id/et_code".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——确认按钮
        self.__pattern_ok = r'text="确定" resource-id="com.chinamworld.main:id/btn_confirm".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——验证码序号
        self.__pattern_verify_seq = r'已向您手机号.+?发送序号为(\d+)的验证码'
        # 密码页面——密码控件
        self.__pattern_code = r'resource-id="com.chinamworld.main:id/et_code".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——图形验证码控件
        self.__pattern_pic_code = r'text="请输入右侧图片的字符" resource-id="com.chinamworld.main:id/native_graph_et".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——取消按钮
        self.__pattern_cancle = r'text="取消" resource-id="android:id/button2".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 密码页面——图片区域
        self.__pattern_pic_rect = r'resource-id="com.chinamworld.main:id/native_graph_iv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 查询结果页面——查询按钮
        self.__pattern_check_result_btn = r'text="查询转账结果" resource-id="com.chinamworld.main:id/btn_right3".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 查询结果页面——取消按钮
        self.__pattern_check_result_cancle_btn = r'text="取消" resource-id="com.chinamworld.main:id/dlg_left_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 查询结果页面——确认按钮
        self.__pattern_check_result_sure_btn = r'text="确定" resource-id="com.chinamworld.main:id/dlg_right_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 异常界面
        self.__pattern_exception = r'text="关闭"\s+resource-id="com.chinamworld.main:id/dlg_right_tv".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
        # 支付页面——关闭框框
        self.__pattern_close_verify_page = r'resource-id="com.chinamworld.main:id/iv_close".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'

        self.__keyboardocr = KeyBoardOcr()

    def check_surplus(self):
        self.driver.power_on_screen()
        self.driver.unlock()
        logger.info('开始通过农业银行查询余额')
        # 回到首页
        try:
            xml = self._enter_main_page()

            # 从待转账页面进入转账页面
            xml = self.__enter_transfer(xml)

            # 转账页面
            return float(self.__transfer(xml, '', '', '', '', True)[0]), ''
        except BaseException as e:
            logger.error(f'查询余额失败,{e}')
            return '', str(e)

    def transfer_cash(self, reciver, card, money, taskid, bank_name):
        self.taskid = taskid
        self.driver.power_on_screen()
        self.driver.unlock()
        self._remainds = ''
        logger.info(f'开始通过平安银行给{reciver}的{bank_name}卡{card}转账{money}元')
        while True:
            try:
                xml = self._enter_main_page()

                # 从待转账页面进入转账页面
                xml = self.__enter_transfer(xml)

                # 转账页面
                self._remainds, xml = self.__transfer(xml, reciver, card,
                                                      str(money), bank_name)

                # 支付
                xml = self.__pay(self.k_passwd, xml)

                # 查询结果
                status, reason = self.check_result(xml)
                if status is True:
                    self._remainds = round(
                        float(self._remainds) - float(money), 2)
                return self._remainds, OK, reason
            except MyError as e:
                e = f'手机原因: {e}'
                logger.error(e)
                logger.error(traceback.format_exc())
                return self._remainds, PHONE_ERR, str(e)
            except NomoneyError as e:
                e = '卡余额不足'
                logger.error(e)
                logger.error(traceback.format_exc())
                return self._remainds, NO_MONEY, str(e)
            except UserError as e:
                e = f'用户原因: {e}'
                logger.error(e)
                logger.error(traceback.format_exc())
                return self._remainds, USER_ERR, str(e)
            except RePlayError as e:
                logger.info('重新开始操作app')
            except BaseException as e:
                e = f'其他原因: {e}'
                logger.error(e)
                logger.error(traceback.format_exc())
                return self._remainds, PHONE_ERR, str(e)

    def check_result(self, xml):
        start_time = time.time()
        while time.time() - start_time < 180:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                logger.info()
                start_time = time.time()
                continue
            elif '转账已受理' in xml or '转账成功' in xml:
                self.send_status(self.taskid, 0)
                ret = '转账成功'
                logger.info(ret)
                self.__click(re.findall(self.__pattern_complish, xml)[0])
                return True, ret
            elif '账号户名不符' in xml:
                self.send_status(self.taskid, 0)
                ret = '账号户名不符'
                logger.info(ret)
                self.__click(re.findall(self.__pattern_back, xml)[0])
                return False, ret
            elif '通讯中断啦' in xml:
                self.send_status(self.taskid, 0)
                raise UserError(f'用户未点击k宝的ok键')
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('查询转账结果超时')

    # 支付页面
    def __pay(self, code, xml):
        # 密码输入
        logger.info(f'输入支付密码 ')
        cur = DIGITAL if self.kcolor == WHITE else CHARACTER
        pay_digit2char = self.white_pay_digit2char if self.kcolor == WHITE else self.black_pay_digit2char  # 数字和字母键盘的切换按钮
        pay_digit_keyboard = self.white_pay_digit_keyboard if self.kcolor == WHITE else self.black_pay_digit_keyboard  #数字键盘
        pay_char_keyboard = self.white_pay_char_keyboard if self.kcolor == WHITE else self.black_pay_char_keyboard  # 字母键盘
        # 数字键盘识别码
        pay_digit_keyboard_templateSign = self.white_pay_digit_keyboard_templateSign if self.kcolor == WHITE else self.black_pay_digit_keyboard_templateSign
        # 字母键盘识别码
        pay_char_keyboard_templateSign = self.white_pay_char_keyboard_templateSign if self.kcolor == WHITE else self.black_pay_char_keyboard_templateSign
        pay_keyboard = pay_digit_keyboard if self.kcolor == WHITE else pay_char_keyboard  # 当前键盘
        keyboard = None
        for c in code:
            if c.isdigit() and cur == CHARACTER:  # 切换数字键盘
                logger.info(f'切换数字键盘 {c.isdigit()} {cur}')
                self.driver.click(pay_digit2char[0] * self.driver.width(),
                                  pay_digit2char[1] * self.driver.height())
                keyboard = KeyBoardOcr.Ocr(self.driver.screencap(),
                                           pay_digit_keyboard_templateSign)
                logger.info(f'百度识别的键盘位置为:{keyboard}')
                pay_keyboard = pay_digit_keyboard
                cur = DIGITAL
            elif c.isalpha() and cur == DIGITAL:  # 切换字母键盘
                logger.info(f'切换字母键盘 {c.isalpha()} {cur}')
                self.driver.click(pay_digit2char[0] * self.driver.width(),
                                  pay_digit2char[1] * self.driver.height())
                keyboard = KeyBoardOcr.Ocr(self.driver.screencap(),
                                           pay_char_keyboard_templateSign)
                logger.info(f'百度识别的键盘位置为:{keyboard}')
                pay_keyboard = pay_char_keyboard
                cur = CHARACTER

            if not keyboard:
                keyboard = KeyBoardOcr.Ocr(
                    self.driver.screencap(), pay_digit_keyboard_templateSign if
                    self.kcolor == WHITE else pay_char_keyboard_templateSign)
                logger.info(f'百度识别的键盘位置为:{keyboard}')

            for i in range(3):
                if c in keyboard:
                    pos = pay_keyboard[keyboard[c]]
                    pos = (int(pos[0] * self.driver.width()),
                           int(pos[1] * self.driver.height()))
                    self.driver.click(pos[0], pos[1])
                    logger.info(f'{c}{pos}')
                    break
                keyboard = KeyBoardOcr.Ocr(
                    self.driver.screencap(), pay_char_keyboard_templateSign
                    if c.isalpha() else pay_digit_keyboard_templateSign)
            else:
                raise MyError('无法在支付%s键盘识别字母%s, %s' %
                              ('数字' if cur == DIGITAL else '字母', c, keyboard))
        self.driver.click(self.pay_btn[0] * self.driver.width(),
                          self.pay_btn[1] * self.driver.height())
        self.send_status(self.taskid, 2)

    # 处理随机弹出页面:如更新,登录
    def __handle_random_page(self, activity, xml='', back=False):
        xml = self.driver.get_xml() if xml == '' else xml
        if re.search(self.__pattern_sure, xml) and '会话超时' in xml:
            logger.info(f'会话超时,点击确定按钮')
            self.__click(re.findall(self.__pattern_sure, xml)[0])
            raise RePlayError('')
        elif re.search(self.__pattern_cancle, xml) and ('确定' not in xml
                                                        or back):
            logger.info('点击取消')
            self.__click(re.findall(self.__pattern_cancle, xml)[0])
            return True
        elif re.search(self.__pattern_sure, xml):
            if back or '确定转账' in xml:
                if back and '确定转账' in xml:
                    return False
                if re.search(self.__pattern_sure, xml):
                    self.__click(re.findall(self.__pattern_sure, xml)[0])
                elif re.search(self.__pattern_next_pay, xml):
                    self.__click(re.findall(self.__pattern_next_pay, xml)[0])
                return True
            elif '请输入收款账户' in xml:
                raise UserError(f"用户未提供收款账户")
            elif '请输入收款方' in xml:
                raise UserError(f'用户未提供收款账户')
            elif '请选择或输入转账金额' in xml:
                raise UserError(f'用户未提供转账金额')
            elif '未搜索到您的k宝' in xml:
                raise UserError(f'未打开k宝')
            elif '蓝牙k宝连接顿开' in xml:
                raise UserError(f'蓝牙k宝连接顿开')
            else:
                return False
        elif back is True and '继续转账' in xml and re.search(
                self.__pattern_complish, xml):
            logger.info(f'点击完成 ')
            self.__click(re.findall(self.__pattern_complish, xml)[0])
            return True
        elif (back is True or '会话超时,请重新登录' in xml) and re.search(
                self.__pattern_back, xml):
            logger.info(f'点击返回 ')
            self.__click(re.findall(self.__pattern_back, xml)[0])
            return True
        elif activity == 'com.android.bankabc.MainActivity' and re.search(
                self.__pattern_login, xml):
            if back is True:  # 在登录界面处于返回状态,则按返回图标,  返回键在此界面没有用
                if re.search(self.__pattern_back_edit, xml):
                    self.__click(re.findall(self.__pattern_back_edit, xml)[0])
                    return True
                else:
                    return False
            else:
                logger.info(f'进入登录页面')
                self.__click(re.findall(self.__pattern_pw_edit, xml)[0])
                time.sleep(1)
                cur = DIGITAL
                keyboard = KeyBoardOcr.Ocr(
                    self.driver.screencap(),
                    self.login_digit_keyboard_templateSign)
                login_keyboard = self.login_digit_keyboard
                for c in self.login_passwd:
                    if c.isdigit() and cur == CHARACTER:  # 切换数字键盘
                        logger.info(f'切换数字键盘 {c.isdigit()} {cur}')
                        self.driver.click(
                            self.login_char2digit[0] * self.driver.width(),
                            self.login_char2digit[1] * self.driver.height())
                        keyboard = KeyBoardOcr.Ocr(
                            self.driver.screencap(),
                            self.login_digit_keyboard_templateSign)
                        login_keyboard = self.login_digit_keyboard
                        cur = DIGITAL
                    elif c.isalpha() and cur == DIGITAL:  # 切换字母键盘
                        logger.info(f'切换字母键盘 {c.isalpha()} {cur}')
                        self.driver.click(
                            self.login_digit2char[0] * self.driver.width(),
                            self.login_digit2char[1] * self.driver.height())
                        keyboard = KeyBoardOcr.Ocr(
                            self.driver.screencap(),
                            self.login_char_keyboard_templateSign)
                        login_keyboard = self.login_char_keyboard
                        cur = CHARACTER

                    for i in range(3):
                        if c in keyboard:
                            pos = login_keyboard[keyboard[c]]
                            pos = (int(pos[0] * self.driver.width()),
                                   int(pos[1] * self.driver.height()))
                            self.driver.click(pos[0], pos[1])
                            logger.info(f'{c}{pos}')
                            break
                        templateSign = self.login_char_keyboard_templateSign if c.isalpha(
                        ) else self.login_digit_keyboard_templateSign
                        keyboard = KeyBoardOcr.Ocr(self.driver.screencap(),
                                                   templateSign)
                    else:
                        xml = self.driver.get_xml()
                        if re.search(self.__pattern_login, xml):
                            raise MyError('无法在登录%s键盘识别字母%s, %s' %
                                          ('数字' if cur == DIGITAL else '字母', c,
                                           keyboard))
                        elif '请输入或选择收款方' in xml:  # 此时进入了转账页面
                            logger.info(f'走出登录页面')
                            return True
                self.driver.click(self.login_btn[0] * self.driver.width(),
                                  self.login_btn[1] * self.driver.height())
                time.sleep(1)
            return True
        return False

    # 待转账页面进入正式转账页面
    def __enter_transfer(self, xml):
        start_time = time.time()
        while time.time() - start_time < 50:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif cur_activity == 'com.android.bankabc.MainActivity':  # 待转账页面和转账页面
                if re.search(self.__pattern_reciver, xml):
                    logger.info('进入正式转账页面')
                    return xml
                ret = re.findall(self.__pattern_transfer_btn, xml)
                if len(ret) == 2 and '他行转本行' in xml:
                    logger.info('点击待转账页面的转账按钮')
                    self.__click(ret[1])
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('从待转账页面进入转账页面超时')

    # 转账页面
    def __transfer(self,
                   xml,
                   name,
                   card,
                   money,
                   bank_name,
                   only4remainds=False):
        start_time = time.time()
        self._remainds = ''
        while time.time() - start_time < 100:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml):
                start_time = time.time()
                continue
            elif '提交了一笔转相同' in xml and re.search(self.__pattern_sure, xml):
                self.__click(re.findall(self.__pattern_sure, xml)[0])
            elif cur_activity == 'com.android.bankabc.MainActivity' and '请输入转账金额' in xml:
                # 正则表达式不稳定
                ret_remainds = re.findall(self.__pattern_remainds, xml)
                if len(ret_remainds) != 1:
                    logger.error(f'未找到余额, {ret_remainds}')
                    continue

                if only4remainds:
                    logger.info(f'余额:{ret_remainds[0]}')
                    return re.sub(r',', '', ret_remainds[0]), xml

                ret_reciver = re.findall(self.__pattern_reciver, xml)
                if len(ret_reciver) != 1:
                    logger.error('未找到收款人控件')
                    continue
                ret_card = re.findall(self.__pattern_card, xml)
                if len(ret_card) != 1:
                    logger.error('未找到卡号控件')
                    continue
                ret_money = re.findall(self.__pattern_money, xml)
                if len(ret_money) != 1:
                    logger.error('未找到转账金额控件')
                    continue

                self._remainds = re.sub(r',', '', ret_remainds[0])
                logger.info(f'余额:{self._remainds}')
                if float(self._remainds) < float(money):
                    raise NomoneyError(f'余额不足{money}元,当前余额{self._remainds}元')

                logger.info('输入收款人 %s' % name)
                self.__click(ret_reciver[0], cnt=2)
                time.sleep(0.2)
                self.driver.input_text(name)
                time.sleep(0.2)

                logger.info('输入卡号 %s' % card)
                self.__click(ret_card[0], cnt=2)
                self.driver.input_text_by_adb(card)
                self.__click(ret_money[0], cnt=2)
                time.sleep(1)

                logger.info('输入金额 %s' % money)
                self.__click(ret_money[0], cnt=2)
                time.sleep(0.2)
                self.driver.input_text_by_adb(money)

                xml = self.driver.get_xml()
                if re.search(self.__pattern_bank_name, xml):
                    logger.info(f'未找到银行{bank_name},搜索去找')
                    self.__click(re.findall(self.__pattern_bank_name, xml)[0],
                                 cnt=1)
                    search_time = time.time()
                    while time.time() - search_time < 10:
                        xml = self.driver.get_xml()
                        if not re.search(self.__pattern_search, xml):
                            continue
                        self.__click(re.findall(self.__pattern_search, xml)[0],
                                     cnt=1)
                        logger.info(f'输入银行{bank_name}')
                        self.driver.input_text(bank_name)

                        search_time = time.time()
                        pattern = f'text="\s*{bank_name}\s*" resource-id="com.android.bankabc:id/tv_title" class="android.widget.TextView".+?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
                        logger.info(f'用正则查找银行 {pattern}')
                        while time.time() - search_time < 10:
                            xml = self.driver.get_xml()
                            if not re.search(pattern, xml):
                                continue
                            self.__click(re.findall(pattern, xml)[0])
                            time.sleep(1)
                            break
                        else:
                            raise UserError(f'卡号{card}对应的银行{bank_name}不存在')
                        break
                    else:
                        raise MyError(f'进入银行选择界面超时')

                self.driver.swip(0.5, 0.7, 0.5, 0.3)
                xml = self.driver.get_xml()
                logger.info('点击下一步')
                ret_next = re.findall(self.__pattern_next, xml)
                if len(ret_next) == 0:
                    raise MyError("未能找到'下一步'按钮")
                self.__click(ret_next[0])
                # 发http请求通知打开k宝
                self.send_status(self.taskid, 1)
                start_time = time.time()
            elif '未搜索到您的K宝' in xml:
                self.send_status(self.taskid, 0)
                raise UserError('用户未打开k宝')
            elif '请输入K宝密码' in xml:
                logger.info('已进入付款页面')
                return self._remainds, xml
            else:
                logger.warning('未知 activity %s' % cur_activity)
        raise MyError('转账页面操作超时')

    # 进入首页
    def _enter_main_page(self):
        #self.driver.stop_app(ABC.package)
        package = self.driver.get_cur_packge()
        # 启动app
        if package != ABC.package:
            self.driver.start_app(ABC.package, ABC.main_activity)
            time.sleep(10)
            cur_time = time.time()
            while time.time() - cur_time <= 30:
                if self.driver.is_app_started('com.android.bankabc'):
                    logger.info('启动app成功')
                    break
                time.sleep(1)
            for i in range(3):
                if self.driver.is_app_started('com.android.bankabc'):
                    break
                time.sleep(0.5)
            else:
                raise MyError('启动APP失败')

        # 进入首页
        start_time = time.time()
        while time.time() - start_time < 30:
            cur_activity = self.driver.get_cur_activity()
            xml = self.driver.get_xml()
            if self.__handle_random_page(cur_activity, xml, True):  # 更新页面
                start_time = time.time()
                continue
            elif cur_activity == 'com.android.bankabc.homepage.HomeActivity' and xml.count(
                    'NAF="true"') >= 5:  # 判断首页刷新完整
                self.driver.click(int(0.3704 * self.driver.width()),
                                  int(0.2899 * self.driver.height()))
                time.sleep(3)  # 预留界面响应时间
            elif cur_activity == 'com.android.bankabc.MainActivity' and '他行转本行' in xml:  # 待转账页面
                logger.info(f'进入待转账页面')
                return xml
            else:
                logger.info('点击返回键')
                self.driver.back()
        raise MyError('回到APP首页超时失败')

    def __click(self, pos, double=False, use_swip=False, time=0.5, cnt=1):
        click_x = (int(pos[0]) + int(pos[2])) // 2
        click_y = (int(pos[1]) + int(pos[3])) // 2
        if use_swip:
            self.driver.swip_pos(click_x, click_y, click_x + 1, click_y + 1,
                                 time)
        else:
            for i in range(cnt):
                self.driver.click(click_x, click_y, double)

        #logger.debug(f'clicked {click_x} {click_y}')

    def send_status(self, taskid, status):
        try:
            r = requests.get(
                url=
                f'http://atf.wuwotech.com/api/ele/kcard?taskId={taskid}&status={status}',
            )
            r.raise_for_status()
        except BaseException as e:
            logger.warning(f'发送k宝状态失败 taskid:{taskid} status: {status}')
Exemplo n.º 5
0
                    y = int(keyboard[c][1] * self.driver.height())
                    #self.driver.swip_pos(x, y, x+1, y+1, 1)
                    self.driver.click(x, y)
                self.driver.click(self.finish[0] * self.driver.width(),
                                  self.finish[1] * self.driver.height())
                time.sleep(0.3)
                logger.info('点击登录按钮')
                self.__click(
                    re.findall(self.__pattern_login, self.driver.get_xml())[0])
                return True
        return False

    def __click(self, pos, double=False):
        click_x = (int(pos[0]) + int(pos[2])) // 2
        click_y = (int(pos[1]) + int(pos[3])) // 2
        self.driver.click(click_x, click_y, double)


if __name__ == '__main__':
    test = '1234567890'
    d = Driver()
    for c in test:
        x = int(PinAn.login_key_board_digital[c][0] * d.width())
        y = int(PinAn.login_key_board_digital[c][1] * d.height())
        # self.driver.swip_pos(x, y, x+1, y+1, 1)
        d.swip_pos(x, y, x + 1, y + 1, 0.7)

    x = int(PinAn.finish[0] * d.width())
    y = int(PinAn.finish[1] * d.height())
    # self.driver.swip_pos(x, y, x+1, y+1, 1)
    d.swip_pos(x, y, x + 1, y + 1, 0.7)