Пример #1
0
 def __init__(self):
     self.session = Request()
     self.request_device_id()
     self.cluster = Cluster()
     self.update_query_interval()
     self.update_query_jobs()
     self.get_query_api_type()
Пример #2
0
 def init_data(self, info):
     self.session = Request()
     self.session.add_response_hook(self.response_login_check)
     self.key = str(info.get('key'))
     self.user_name = info.get('user_name')
     self.password = info.get('password')
     self.type = info.get('type')
Пример #3
0
    def check_item_available(self, item, try_num=0):
        session = Request()
        response = session.get(API_CHECK_CDN_AVAILABLE.format(item),
                               headers={'Host': HOST_URL_OF_12306},
                               timeout=self.check_time_out,
                               verify=False)

        if response.status_code == 200:
            if not self.is_recheck:
                self.available_items.append(item)
            else:
                self.recheck_available_items.append(item)
            if not self.is_ready: self.check_is_ready()
        elif try_num < self.retry_num:  # 重试
            stay_second(self.safe_stay_time)
            return self.check_item_available(item, try_num + 1)
        else:
            if not self.is_recheck:
                self.unavailable_items.append(item)
            else:
                self.recheck_unavailable_items.append(item)
        if not self.is_recheck and (
                not self.last_check_at or
            (time_now() - self.last_check_at).seconds > self.save_second):
            self.save_available_items()
        stay_second(self.safe_stay_time)
Пример #4
0
    def __init__(self, info, user):
        self.session = Request()
        self.heartbeat = user.heartbeat

        self.key = info.get('key')
        self.user_name = info.get('user_name')
        self.password = info.get('password')
        self.user = user
Пример #5
0
class OCR:
    """
    图片识别
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def get_img_position(cls, img):
        """
        获取图像坐标
        :param img_path:
        :return:
        """
        self = cls()
        if Config().AUTO_CODE_PLATFORM == 'free':
            return self.get_image_by_free_site(img)
        return self.get_img_position_by_ruokuai(img)

    def get_img_position_by_ruokuai(self, img):
        ruokuai_account = Config().AUTO_CODE_ACCOUNT
        soft_id = '119671'
        soft_key = '6839cbaca1f942f58d2760baba5ed987'
        rc = RKClient(ruokuai_account.get('user'), ruokuai_account.get('pwd'),
                      soft_id, soft_key)
        result = rc.rk_create(img, 6113)
        if "Result" in result:
            return self.get_image_position_by_offset(list(result['Result']))
        CommonLog.print_auto_code_fail(
            result.get("Error", CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))
        return None

    def get_image_position_by_offset(self, offsets):
        positions = []
        width = 75
        height = 75
        for offset in offsets:
            random_x = random.randint(-5, 5)
            random_y = random.randint(-5, 5)
            offset = int(offset)
            x = width * ((offset - 1) % 4 + 1) - width / 2 + random_x
            y = height * math.ceil(offset / 4) - height / 2 + random_y
            positions.append(int(x))
            positions.append(int(y))
        return positions

    def get_image_by_free_site(self, img):
        data = {'img': img}
        response = self.session.post(API_FREE_CODE_QCR_API, data=data)
        result = response.json()
        if result.get('msg') == 'success':
            pos = result.get('result')
            return self.get_image_position_by_offset(pos)

        CommonLog.print_auto_code_fail(
            CommonLog.MESSAGE_GET_RESPONSE_FROM_FREE_AUTO_CODE)
        return None
Пример #6
0
class Notification():
    """
    通知类
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def voice_code(cls, phone, name='', content=''):
        self = cls()
        self.send_voice_code_of_yiyuan(phone, name=name, content=content)

    def send_voice_code_of_yiyuan(self, phone, name='', content=''):
        """
        发送语音验证码
        购买地址 https://market.aliyun.com/products/57126001/cmapi019902.html?spm=5176.2020520132.101.5.37857218O6iJ3n
        :return:
        """
        appcode = config.NOTIFICATION_API_APP_CODE
        if not appcode:
            CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush()
            return False
        body = {
            'userName': name,
            'mailNo': content
        }
        params = {
            'content': body,
            'mobile': phone,
            'sex': 2,
            'tNum': 'T170701001056'
        }
        response = self.session.request(url=API_NOTIFICATION_BY_VOICE_CODE + urllib.parse.urlencode(params),
                                        method='GET', headers={
                'Authorization': 'APPCODE {}'.format(appcode)
            })
        response_message = '-'
        result = {}
        try:
            result = response.json()
            response_message = result['showapi_res_body']['remark']
        except:
            pass
        if response.status_code == 401 or response.status_code == 403:
            return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_FORBID).flush()
        if response.status_code == 200 and 'showapi_res_body' in result and result['showapi_res_body'].get('flag'):
            CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format(response_message)).flush()
            return True
        else:
            return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(response_message)).flush()
Пример #7
0
class UserJob:
    heartbeat = 60 * 2  # 心跳保持时长
    heartbeat_interval = 5
    key = None
    user_name = ''
    password = ''
    user = None
    info = {}  # 用户信息
    last_heartbeat = None
    is_ready = False
    passengers = []
    retry_time = 5

    # Init page
    global_repeat_submit_token = None
    ticket_info_for_passenger_form = None
    order_request_dto = None

    def __init__(self, info, user):
        self.session = Request()
        self.heartbeat = user.heartbeat

        self.key = info.get('key')
        self.user_name = info.get('user_name')
        self.password = info.get('password')
        self.user = user

    def run(self):
        # load user
        if not Const.IS_TEST:
            self.load_user()
        self.start()

    def start(self):
        """
        检测心跳
        :return:
        """
        while True:
            app_available_check()
            self.check_heartbeat()
            if Const.IS_TEST: return
            sleep(self.heartbeat_interval)

    def check_heartbeat(self):
        # 心跳检测
        if self.last_heartbeat and (
                time_now() - self.last_heartbeat).seconds < self.heartbeat:
            return True
        if self.is_first_time() or not self.check_user_is_login():
            self.handle_login()

        self.is_ready = True
        UserLog.add_quick_log(
            UserLog.MESSAGE_USER_HEARTBEAT_NORMAL.format(
                self.get_name(), self.heartbeat)).flush()
        self.last_heartbeat = time_now()

    # def init_cookies
    def is_first_time(self):
        return not path.exists(self.get_cookie_path())

    def handle_login(self):
        UserLog.print_start_login(user=self)
        self.login()

    def login(self):
        """
        获取验证码结果
        :return 权限校验码
        """
        data = {
            'username': self.user_name,
            'password': self.password,
            'appid': 'otn'
        }
        answer = AuthCode.get_auth_code(self.session)
        data['answer'] = answer
        response = self.session.post(API_BASE_LOGIN.get('url'), data)
        result = response.json()
        if result.get('result_code') == 0:  # 登录成功
            """
            login 获得 cookie uamtk
            auth/uamtk      不请求,会返回 uamtk票据内容为空
            /otn/uamauthclient 能拿到用户名
            """
            new_tk = self.auth_uamtk()
            user_name = self.auth_uamauthclient(new_tk)
            self.update_user_info({'user_name': user_name})
            self.login_did_success()
        elif result.get('result_code') == 2:  # 账号之内错误
            # 登录失败,用户名或密码为空
            # 密码输入错误
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message')))
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message', result.get('message', '-'))))

        return False

        pass

    def check_user_is_login(self):
        response = self.session.get(API_USER_CHECK.get('url'))
        is_login = response.json().get('data').get('flag', False)
        if is_login:
            self.save_user()
        return is_login

    def auth_uamtk(self):
        response = self.session.post(API_AUTH_UAMTK.get('url'),
                                     {'appid': 'otn'})
        result = response.json()
        if result.get('newapptk'):
            return result.get('newapptk')
        # TODO 处理获取失败情况
        return False

    def auth_uamauthclient(self, tk):
        response = self.session.post(API_AUTH_UAMAUTHCLIENT.get('url'),
                                     {'tk': tk})
        result = response.json()
        if result.get('username'):
            return result.get('username')
        # TODO 处理获取失败情况
        return False

    def login_did_success(self):
        """
        用户登录成功
        :return:
        """
        self.welcome_user()
        self.save_user()
        self.get_user_info()
        pass

    def welcome_user(self):
        UserLog.print_welcome_user(self)
        pass

    def get_cookie_path(self):
        return config.USER_DATA_DIR + self.user_name + '.cookie'

    def update_user_info(self, info):
        self.info = {**self.info, **info}

    def get_name(self):
        return self.info.get('user_name')

    def save_user(self):
        with open(self.get_cookie_path(), 'wb') as f:
            pickle.dump(self.session.cookies, f)

    def did_loaded_user(self):
        """
        恢复用户成功
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_LOADED_USER.format(self.user_name))
        if self.check_user_is_login():
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_SUCCESS.format(self.user_name))
            self.get_user_info()
            UserLog.print_welcome_user(self)
        else:
            UserLog.add_quick_log(UserLog.MESSAGE_LOADED_USER_BUT_EXPIRED)

    def get_user_info(self):
        response = self.session.get(API_USER_INFO.get('url'))
        result = response.json()
        user_data = result.get('data.userDTO.loginUserDTO')
        if user_data:
            self.update_user_info({
                **user_data,
                **{
                    'user_name': user_data.get('name')
                }
            })
            return True
        return None

    def load_user(self):
        cookie_path = self.get_cookie_path()
        if path.exists(cookie_path):
            with open(self.get_cookie_path(), 'rb') as f:
                self.session.cookies.update(pickle.load(f))
                self.did_loaded_user()
                return True
        return None

    def check_is_ready(self):
        return self.is_ready

    def get_user_passengers(self):
        if self.passengers: return self.passengers
        response = self.session.post(API_USER_PASSENGERS)
        result = response.json()
        if result.get('data.normal_passengers'):
            self.passengers = result.get('data.normal_passengers')
            return self.passengers
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format(
                    result.get('messages', '-'), self.retry_time))
            stay_second(self.retry_time)
            return self.get_user_passengers()

    def get_passengers_by_members(self, members):
        """
        获取格式化后的乘客信息
        :param members:
        :return:
        [{
            name: '项羽',
            type: 1,
            id_card: 0000000000000000000,
            type_text: '成人'
        }]
        """
        self.get_user_passengers()
        results = []
        for member in members:
            child_check = array_dict_find_by_key_value(results, 'name', member)
            if child_check:
                new_member = child_check.copy()
                new_member['type'] = UserType.CHILD
                new_member['type_text'] = dict_find_key_by_value(
                    UserType.dicts, int(new_member['type']))
            else:
                passenger = array_dict_find_by_key_value(
                    self.passengers, 'passenger_name', member)
                if not passenger:
                    UserLog.add_quick_log(
                        UserLog.MESSAGE_USER_PASSENGERS_IS_INVALID.format(
                            self.user_name,
                            member)).flush(exit=True)  # TODO 需要优化
                new_member = {
                    'name':
                    passenger.get('passenger_name'),
                    'id_card':
                    passenger.get('passenger_id_no'),
                    'id_card_type':
                    passenger.get('passenger_id_type_code'),
                    'mobile':
                    passenger.get('mobile_no'),
                    'type':
                    passenger.get('passenger_type'),
                    'type_text':
                    dict_find_key_by_value(
                        UserType.dicts, int(passenger.get('passenger_type')))
                }
            results.append(new_member)

        return results

    def request_init_dc_page(self):
        """
        请求下单页面 拿到 token
        :return:
        """
        data = {'_json_att': ''}
        response = self.session.post(API_INITDC_URL, data)
        html = response.text
        token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html)
        form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html)
        order = re.search(r'var orderRequestDTO *= *(\{.+\})', html)
        # 系统忙,请稍后重试
        if html.find('系统忙,请稍后重试') != -1:
            OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL
                                   ).flush()  # 重试无用,直接跳过
            return False
        try:
            self.global_repeat_submit_token = token.groups()[0]
            self.ticket_info_for_passenger_form = json.loads(
                form.groups()[0].replace("'", '"'))
            self.order_request_dto = json.loads(order.groups()[0].replace(
                "'", '"'))
        except:
            pass  # TODO Error

        return True
Пример #8
0
 def init_data(self, info):
     self.session = Request()
     self.key = str(info.get('key'))
     self.user_name = info.get('user_name')
     self.password = info.get('password')
Пример #9
0
class UserJob:
    # heartbeat = 60 * 2  # 心跳保持时长
    is_alive = True
    check_interval = 5
    key = None
    user_name = ''
    password = ''
    user = None
    info = {}  # 用户信息
    last_heartbeat = None
    is_ready = False
    user_loaded = False  # 用户是否已加载成功
    passengers = []
    retry_time = 3
    login_num = 0  # 尝试登录次数

    # Init page
    global_repeat_submit_token = None
    ticket_info_for_passenger_form = None
    order_request_dto = None

    cluster = None
    lock_init_user_time = 3 * 60
    cookie = False

    def __init__(self, info):
        self.cluster = Cluster()
        self.init_data(info)

    def init_data(self, info):
        self.session = Request()
        self.session.add_response_hook(self.response_login_check)
        self.key = str(info.get('key'))
        self.user_name = info.get('user_name')
        self.password = info.get('password')

    def update_user(self):
        from py12306.user.user import User
        self.user = User()
        self.load_user()

    def run(self):
        # load user
        self.update_user()
        self.start()

    def start(self):
        """
        检测心跳
        :return:
        """
        while True and self.is_alive:
            app_available_check()
            if Config().is_slave():
                self.load_user_from_remote()
            else:
                if Config().is_master() and not self.cookie:
                    self.load_user_from_remote()  # 主节点加载一次 Cookie
                self.check_heartbeat()
            if Const.IS_TEST: return
            stay_second(self.check_interval)

    def check_heartbeat(self):
        # 心跳检测
        if self.get_last_heartbeat() and (
                time_int() -
                self.get_last_heartbeat()) < Config().USER_HEARTBEAT_INTERVAL:
            return True
        # 只有主节点才能走到这
        if self.is_first_time() or not self.check_user_is_login():
            if not self.handle_login(): return

        self.user_did_load()
        message = UserLog.MESSAGE_USER_HEARTBEAT_NORMAL.format(
            self.get_name(),
            Config().USER_HEARTBEAT_INTERVAL)
        UserLog.add_quick_log(message).flush()

    def get_last_heartbeat(self):
        if Config().is_cluster_enabled():
            return int(
                self.cluster.session.get(Cluster.KEY_USER_LAST_HEARTBEAT, 0))

        return self.last_heartbeat

    def set_last_heartbeat(self, time=None):
        time = time if time != None else time_int()
        if Config().is_cluster_enabled():
            self.cluster.session.set(Cluster.KEY_USER_LAST_HEARTBEAT, time)
        self.last_heartbeat = time

    # def init_cookies
    def is_first_time(self):
        if Config().is_cluster_enabled():
            return not self.cluster.get_user_cookie(self.key)
        return not path.exists(self.get_cookie_path())

    def handle_login(self, expire=False):
        if expire: UserLog.print_user_expired()
        self.is_ready = False
        UserLog.print_start_login(user=self)
        return self.login()

    def login(self):
        """
        获取验证码结果
        :return 权限校验码
        """
        data = {
            'username': self.user_name,
            'password': self.password,
            'appid': 'otn'
        }
        self.request_device_id()
        answer = AuthCode.get_auth_code(self.session)
        data['answer'] = answer
        response = self.session.post(API_BASE_LOGIN.get('url'), data)
        result = response.json()
        if result.get('result_code') == 0:  # 登录成功
            """
            login 获得 cookie uamtk
            auth/uamtk      不请求,会返回 uamtk票据内容为空
            /otn/uamauthclient 能拿到用户名
            """
            new_tk = self.auth_uamtk()
            user_name = self.auth_uamauthclient(new_tk)
            self.update_user_info({'user_name': user_name})
            self.login_did_success()
            return True
        elif result.get('result_code') == 2:  # 账号之内错误
            # 登录失败,用户名或密码为空
            # 密码输入错误
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message'))).flush()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get(
                        'result_message',
                        result.get(
                            'message',
                            CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR)))).flush()

        return False

    def check_user_is_login(self):
        response = self.session.get(API_USER_LOGIN_CHECK)
        is_login = response.json().get('data.is_login', False) == 'Y'
        if is_login:
            self.save_user()
            self.set_last_heartbeat()
            return self.get_user_info(
            )  # 检测应该是不会维持状态,这里再请求下个人中心看有没有用,01-10 看来应该是没用  01-22 有时拿到的状态 是已失效的再加上试试

        return is_login

    def auth_uamtk(self):
        response = self.session.post(API_AUTH_UAMTK.get('url'),
                                     {'appid': 'otn'})
        result = response.json()
        if result.get('newapptk'):
            return result.get('newapptk')
        # TODO 处理获取失败情况
        return False

    def auth_uamauthclient(self, tk):
        response = self.session.post(API_AUTH_UAMAUTHCLIENT.get('url'),
                                     {'tk': tk})
        result = response.json()
        if result.get('username'):
            return result.get('username')
        # TODO 处理获取失败情况
        return False

    def request_device_id(self):
        """
        获取加密后的浏览器特征 ID
        :return:
        """
        params = {
            "algID": self.request_alg_id(),
            "timestamp": int(time.time() * 1000)
        }
        params = dict(params, **self._get_hash_code_params())
        response = self.session.get(API_GET_BROWSER_DEVICE_ID, params=params)
        if response.text.find('callbackFunction') >= 0:
            result = response.text[18:-2]
            try:
                result = json.loads(result)
                self.session.cookies.update({
                    'RAIL_EXPIRATION':
                    result.get('exp'),
                    'RAIL_DEVICEID':
                    result.get('dfp'),
                    # 'RAIL_EXPIRATION': '1554474881858',
                    # 'RAIL_DEVICEID': 'AuT-Gn6_zBqrgut3m5pj-OtRGGHDAKXfCDKI3SlDT98JBD-XzxPH08tjaplcAW5aKb8nyX90r92psp5QpwRIGTn6XeIwiQxvuwEnqseza6mPSAu_gmrGCLVpFvCbDUky4EB-UTjDH-ozAHx1oaz5KkvGgakW0Jou',
                })
            except:
                return False

    def request_alg_id(self):
        response = self.session.get("https://kyfw.12306.cn/otn/HttpZF/GetJS")
        result = re.search(r'algID\\x3d(.*?)\\x26', response.text)
        try:
            return result.group(1)
        except (IndexError, AttributeError) as e:
            pass
        return ""

    def _get_hash_code_params(self):
        from collections import OrderedDict
        data = {
            'adblock':
            '0',
            'browserLanguage':
            'en-US',
            'cookieEnabled':
            '1',
            'custID':
            '133',
            'doNotTrack':
            'unknown',
            'flashVersion':
            '0',
            'javaEnabled':
            '0',
            'jsFonts':
            'c227b88b01f5c513710d4b9f16a5ce52',
            'localCode':
            '3232236206',
            'mimeTypes':
            '52d67b2a5aa5e031084733d5006cc664',
            'os':
            'MacIntel',
            'platform':
            'WEB',
            'plugins':
            'd22ca0b81584fbea62237b14bd04c866',
            'scrAvailSize':
            str(random.randint(500, 1000)) + 'x1920',
            'srcScreenSize':
            '24xx1080x1920',
            'storeDb':
            'i1l1o1s1',
            'timeZone':
            '-8',
            'touchSupport':
            '99115dfb07133750ba677d055874de87',
            'userAgent':
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.'
            + str(random.randint(5000, 7000)) + '.0 Safari/537.36',
            'webSmartID':
            'f4e3b7b14cc647e30a6267028ad54c56',
        }
        data_trans = {
            'browserVersion': 'd435',
            'touchSupport': 'wNLf',
            'systemLanguage': 'e6OK',
            'scrWidth': 'ssI5',
            'openDatabase': 'V8vl',
            'scrAvailSize': 'TeRS',
            'hasLiedResolution': '3neK',
            'hasLiedOs': 'ci5c',
            'timeZone': 'q5aJ',
            'userAgent': '0aew',
            'userLanguage': 'hLzX',
            'jsFonts': 'EOQP',
            'scrAvailHeight': '88tV',
            'browserName': '-UVA',
            'cookieCode': 'VySQ',
            'online': '9vyE',
            'scrAvailWidth': 'E-lJ',
            'flashVersion': 'dzuS',
            'scrDeviceXDPI': '3jCe',
            'srcScreenSize': 'tOHY',
            'storeDb': 'Fvje',
            'doNotTrack': 'VEek',
            'mimeTypes': 'jp76',
            'sessionStorage': 'HVia',
            'cookieEnabled': 'VPIf',
            'os': 'hAqN',
            'hasLiedLanguages': 'j5po',
            'hasLiedBrowser': '2xC5',
            'webSmartID': 'E3gR',
            'appcodeName': 'qT7b',
            'javaEnabled': 'yD16',
            'plugins': 'ks0Q',
            'appMinorVersion': 'qBVW',
            'cpuClass': 'Md7A',
            'indexedDb': '3sw-',
            'adblock': 'FMQw',
            'localCode': 'lEnu',
            'browserLanguage': 'q4f3',
            'scrHeight': '5Jwy',
            'localStorage': 'XM7l',
            'historyList': 'kU5z',
            'scrColorDepth': "qmyu"
        }
        data = OrderedDict(data)
        data_str = ''
        params = {}
        for key, item in data.items():
            data_str += key + item
            key = data_trans[key] if key in data_trans else key
            params[key] = item
        data_str = self._encode_data_str(data_str)
        data_str_len = len(data_str)
        data_str_len_div = int(data_str_len / 2)
        if data_str_len % 2 == 0:
            data_str = data_str[data_str_len_div:data_str_len] + data_str[
                0:data_str_len_div]
        else:
            data_str = data_str[data_str_len_div + 1:data_str_len] + \
                       data_str[data_str_len_div] + data_str[0:data_str_len_div]

        data_str = self._encode_data_str(data_str)
        data_str_tmp = ""
        for e in range(0, len(data_str)):
            data_str_code = ord(data_str[e])
            data_str_tmp += chr(0) if data_str_code == 127 else chr(
                data_str_code + 1)

        data_str = self._encode_string(data_str_tmp[::-1])
        params['hashCode'] = data_str
        return params

    def _encode_data_str(self, data_str):
        data_str_len = len(data_str)
        data_str_len_tmp = int(
            data_str_len /
            3) if data_str_len % 3 == 0 else int(data_str_len / 3) + 1
        if data_str_len >= 3:
            data_str_e = data_str[0:data_str_len_tmp]
            data_str_f = data_str[data_str_len_tmp:2 * data_str_len_tmp]
            return data_str_f + data_str[
                2 * data_str_len_tmp:data_str_len] + data_str_e
        return data_str

    def _encode_string(self, str):
        import hashlib
        import base64
        result = base64.b64encode(hashlib.sha256(
            str.encode()).digest()).decode()
        return result.replace('+', '-').replace('/', '_').replace('=', '')

    def login_did_success(self):
        """
        用户登录成功
        :return:
        """
        self.login_num += 1
        self.welcome_user()
        self.save_user()
        self.get_user_info()
        self.set_last_heartbeat()
        self.is_ready = True

    def welcome_user(self):
        UserLog.print_welcome_user(self)
        pass

    def get_cookie_path(self):
        return Config().USER_DATA_DIR + self.user_name + '.cookie'

    def update_user_info(self, info):
        self.info = {**self.info, **info}

    def get_name(self):
        return self.info.get('user_name', '')

    def save_user(self):
        if Config().is_master():
            self.cluster.set_user_cookie(self.key, self.session.cookies)
            self.cluster.set_user_info(self.key, self.info)
        with open(self.get_cookie_path(), 'wb') as f:
            pickle.dump(self.session.cookies, f)

    def did_loaded_user(self):
        """
        恢复用户成功
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_LOADED_USER.format(self.user_name)).flush()
        if self.check_user_is_login() and self.get_user_info():
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_SUCCESS.format(
                    self.user_name)).flush()
            UserLog.print_welcome_user(self)
            self.user_did_load()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_BUT_EXPIRED).flush()
            self.set_last_heartbeat(0)

    def user_did_load(self):
        """
        用户已经加载成功
        :return:
        """
        self.is_ready = True
        if self.user_loaded: return
        self.user_loaded = True
        Event().user_loaded({'key': self.key})  # 发布通知

    def get_user_info(self):
        response = self.session.get(API_USER_INFO.get('url'))
        result = response.json()
        user_data = result.get('data.userDTO.loginUserDTO')
        # 子节点访问会导致主节点登录失效 TODO 可快考虑实时同步 cookie
        if user_data:
            self.update_user_info({
                **user_data,
                **{
                    'user_name': user_data.get('name')
                }
            })
            self.save_user()
            return True
        return False

    def load_user(self):
        if Config().is_cluster_enabled(): return
        cookie_path = self.get_cookie_path()

        if path.exists(cookie_path):
            with open(self.get_cookie_path(), 'rb') as f:
                cookie = pickle.load(f)
                self.cookie = True
                self.session.cookies.update(cookie)
                self.did_loaded_user()
                return True
        return None

    def load_user_from_remote(self):
        cookie = self.cluster.get_user_cookie(self.key)
        info = self.cluster.get_user_info(self.key)
        if Config().is_slave() and (not cookie or not info):
            while True:  # 子节点只能取
                UserLog.add_quick_log(
                    UserLog.MESSAGE_USER_COOKIE_NOT_FOUND_FROM_REMOTE.format(
                        self.user_name)).flush()
                stay_second(self.retry_time)
                return self.load_user_from_remote()
        if info: self.info = info
        if cookie:
            self.session.cookies.update(cookie)
            if not self.cookie:  # 第一次加载
                self.cookie = True
                if not Config().is_slave():
                    self.did_loaded_user()
                else:
                    self.is_ready = True  # 设置子节点用户 已准备好
                    UserLog.print_welcome_user(self)
            return True
        return False

    def check_is_ready(self):
        return self.is_ready

    def wait_for_ready(self):
        if self.is_ready: return self
        UserLog.add_quick_log(
            UserLog.MESSAGE_WAIT_USER_INIT_COMPLETE.format(
                self.retry_time)).flush()
        stay_second(self.retry_time)
        return self.wait_for_ready()

    def destroy(self):
        """
        退出用户
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_USER_BEING_DESTROY.format(self.user_name)).flush()
        self.is_alive = False

    def response_login_check(self, response, **kwargs):
        if Config().is_master() and response.json().get(
                'data.noLogin') == 'true':  # relogin
            self.handle_login(expire=True)

    def get_user_passengers(self):
        if self.passengers: return self.passengers
        response = self.session.post(API_USER_PASSENGERS)
        result = response.json()
        if result.get('data.normal_passengers'):
            self.passengers = result.get('data.normal_passengers')
            # 将乘客写入到文件
            with open(Config().USER_PASSENGERS_FILE % self.user_name,
                      'w',
                      encoding='utf-8') as f:
                f.write(
                    json.dumps(self.passengers, indent=4, ensure_ascii=False))
            return self.passengers
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format(
                    result.get('messages',
                               CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR),
                    self.retry_time)).flush()
            if Config().is_slave():
                self.load_user_from_remote()  # 加载最新 cookie
            stay_second(self.retry_time)
            return self.get_user_passengers()

    def get_passengers_by_members(self, members):
        """
        获取格式化后的乘客信息
        :param members:
        :return:
        [{
            name: '项羽',
            type: 1,
            id_card: 0000000000000000000,
            type_text: '成人'
        }]
        """
        self.get_user_passengers()
        results = []
        for member in members:
            is_member_code = is_number(member)
            if not is_member_code:
                child_check = array_dict_find_by_key_value(
                    results, 'name', member)
            if not is_member_code and child_check:
                new_member = child_check.copy()
                new_member['type'] = UserType.CHILD
                new_member['type_text'] = dict_find_key_by_value(
                    UserType.dicts, int(new_member['type']))
            else:
                if is_member_code:
                    passenger = array_dict_find_by_key_value(
                        self.passengers, 'code', member)
                else:
                    passenger = array_dict_find_by_key_value(
                        self.passengers, 'passenger_name', member)
                if not passenger:
                    UserLog.add_quick_log(
                        UserLog.MESSAGE_USER_PASSENGERS_IS_INVALID.format(
                            self.user_name, member)).flush()
                    return False
                new_member = {
                    'name':
                    passenger.get('passenger_name'),
                    'id_card':
                    passenger.get('passenger_id_no'),
                    'id_card_type':
                    passenger.get('passenger_id_type_code'),
                    'mobile':
                    passenger.get('mobile_no'),
                    'type':
                    passenger.get('passenger_type'),
                    'type_text':
                    dict_find_key_by_value(
                        UserType.dicts, int(passenger.get('passenger_type')))
                }
            results.append(new_member)

        return results

    def request_init_dc_page(self):
        """
        请求下单页面 拿到 token
        :return:
        """
        data = {'_json_att': ''}
        response = self.session.post(API_INITDC_URL, data)
        html = response.text
        token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html)
        form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html)
        order = re.search(r'var orderRequestDTO *= *(\{.+\})', html)
        # 系统忙,请稍后重试
        if html.find('系统忙,请稍后重试') != -1:
            OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL
                                   ).flush()  # 重试无用,直接跳过
            return False
        try:
            self.global_repeat_submit_token = token.groups()[0]
            self.ticket_info_for_passenger_form = json.loads(
                form.groups()[0].replace("'", '"'))
            self.order_request_dto = json.loads(order.groups()[0].replace(
                "'", '"'))
        except:
            pass  # TODO Error

        return True
Пример #10
0
class Notification():
    """
    通知类
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def voice_code(cls, phone, name='', content=''):
        self = cls()
        self.send_voice_code_of_yiyuan(phone, name=name, content=content)

    @classmethod
    def dingtalk_webhook(cls, content=''):
        self = cls()
        self.send_dingtalk_by_webbook(content=content)

    @classmethod
    def send_email(cls, to, title='', content=''):
        self = cls()
        self.send_email_by_smtp(to, title, content)

    @classmethod
    def send_to_telegram(cls, content=''):
        self = cls()
        self.send_to_telegram_bot(content=content)

    def send_voice_code_of_yiyuan(self, phone, name='', content=''):
        """
        发送语音验证码
        购买地址 https://market.aliyun.com/products/57126001/cmapi019902.html?spm=5176.2020520132.101.5.37857218O6iJ3n
        :return:
        """
        appcode = Config().NOTIFICATION_API_APP_CODE
        if not appcode:
            CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush()
            return False
        body = {'userName': name, 'mailNo': content}
        params = {
            'content': body,
            'mobile': phone,
            'sex': 2,
            'tNum': 'T170701001056'
        }
        response = self.session.request(
            url=API_NOTIFICATION_BY_VOICE_CODE +
            urllib.parse.urlencode(params),
            method='GET',
            headers={'Authorization': 'APPCODE {}'.format(appcode)})
        result = response.json()
        response_message = result.get('showapi_res_body.remark')
        if response.status_code in [400, 401, 403]:
            return CommonLog.add_quick_log(
                CommonLog.MESSAGE_VOICE_API_FORBID).flush()
        if response.status_code == 200 and result.get('showapi_res_body.flag'):
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format(
                    response_message)).flush()
            return True
        else:
            return CommonLog.add_quick_log(
                CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(
                    response_message)).flush()

    def send_email_by_smtp(self, to, title, content):
        import smtplib
        from email.message import EmailMessage
        to = to if isinstance(to, list) else [to]
        message = EmailMessage()
        message['Subject'] = title
        message['From'] = Config().EMAIL_SENDER
        message['To'] = to
        message.set_content(content)
        try:
            server = smtplib.SMTP(Config().EMAIL_SERVER_HOST)
            server.login(Config().EMAIL_SERVER_USER,
                         Config().EMAIL_SERVER_PASSWORD)
            server.send_message(message)
            server.quit()
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_SEND_EMAIL_SUCCESS).flush()
        except Exception as e:
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()

    def send_dingtalk_by_webbook(self, content):
        from dingtalkchatbot.chatbot import DingtalkChatbot
        webhook = Config().DINGTALK_WEBHOOK
        dingtalk = DingtalkChatbot(webhook)
        dingtalk.send_text(msg=content, is_at_all=True)
        pass

    def send_to_telegram_bot(self, content):
        bot_api_url = Config().TELEGRAM_BOT_API_URL
        if not bot_api_url:
            return False
        data = {'text': content}
        response = self.session.request(url=bot_api_url,
                                        method='POST',
                                        data=data)
        result = response.json().get('result')
        response_status = result.get('statusCode')
        if response_status == 200:
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_SEND_TELEGRAM_SUCCESS).flush()
        else:
            response_error_message = result.get('description')
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_SEND_TELEGRAM_FAIL.format(
                    response_error_message)).flush()
Пример #11
0
class OCR:
    """
    图片识别
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def get_img_position(cls, img):
        """
        获取图像坐标
        :param img_path:
        :return:
        """
        self = cls()
        if Config().AUTO_CODE_PLATFORM == 'free':
            return self.get_image_by_free_site(img)
        return self.get_img_position_by_ruokuai(img)

    def get_img_position_by_ruokuai(self, img):
        ruokuai_account = Config().AUTO_CODE_ACCOUNT
        soft_id = '119671'
        soft_key = '6839cbaca1f942f58d2760baba5ed987'
        rc = RKClient(ruokuai_account.get('user'), ruokuai_account.get('pwd'),
                      soft_id, soft_key)
        result = rc.rk_create(img, 6113)
        if "Result" in result:
            return self.get_image_position_by_offset(list(result['Result']))
        CommonLog.print_auto_code_fail(
            result.get("Error", CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))
        return None

    def get_image_position_by_offset(self, offsets):
        positions = []
        width = 75
        height = 75
        for offset in offsets:
            random_x = random.randint(-5, 5)
            random_y = random.randint(-5, 5)
            offset = int(offset)
            x = width * ((offset - 1) % 4 + 1) - width / 2 + random_x
            y = height * math.ceil(offset / 4) - height / 2 + random_y
            positions.append(int(x))
            positions.append(int(y))
        return positions

    def get_image_by_free_site(self, img):
        """
        本地机器学习打码
        :param img:
        :return:
        """
        # from py12306.helpers.ocr.ml_predict import get_coordinate
        # import base64
        #
        # result = get_coordinate(base64.b64decode(img))
        # result = self.get_image_position_by_offset(result)
        # # CommonLog.print_auth_code_info("验证码识别的结果为:" + result)
        #
        # if result:
        #     return result
        """
        线上打码,可靠性未知
        """
        data = {'img': img}
        response = self.session.post(API_FREE_CODE_QCR_API, data=data)
        result = response.json()
        if result.get('msg') == 'success':
            pos = result.get('result')
            return self.get_image_position_by_offset(pos)
        else:
            from py12306.helpers.ocr.ml_predict import get_coordinate
            import base64

            result = get_coordinate(base64.b64decode(img))
            result = self.get_image_position_by_offset(result)
            # CommonLog.print_auth_code_info("验证码识别的结果为:" + result)

        if result:
            return result

        return None
Пример #12
0
 def __init__(self):
     self.session = Request()
     self.cluster = Cluster()
     self.update_query_interval()
     self.update_query_jobs()
Пример #13
0
class Notification():
    """
    通知类
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def voice_code(cls, phone, name='', content=''):
        self = cls()
        if Config().NOTIFICATION_VOICE_CODE_TYPE == 'dingxin':
            self.send_voice_code_of_dingxin(phone, name=name, info=content)
        else:
            self.send_voice_code_of_yiyuan(phone, name=name, content=content)

    @classmethod
    def dingtalk_webhook(cls, content=''):
        self = cls()
        self.send_dingtalk_by_webbook(content=content)

    @classmethod
    def send_email(cls, to, title='', content=''):
        self = cls()
        self.send_email_by_smtp(to, title, content)

    @classmethod
    def send_to_telegram(cls, content=''):
        self = cls()
        self.send_to_telegram_bot(content=content)

    @classmethod
    def server_chan(cls, skey='', title='', content=''):
        self = cls()
        self.send_serverchan(skey=skey, title=title, content=content)

    @classmethod
    def push_bear(cls, skey='', title='', content=''):
        self = cls()
        self.send_pushbear(skey=skey, title=title, content=content)

    @classmethod
    def push_bark(cls, content=''):
        self = cls()
        self.push_to_bark(content)

    def send_voice_code_of_yiyuan(self, phone, name='', content=''):
        """
        发送语音验证码
        购买地址 https://market.aliyun.com/products/57126001/cmapi019902.html?spm=5176.2020520132.101.5.37857218O6iJ3n
        :return:
        """
        appcode = Config().NOTIFICATION_API_APP_CODE
        if not appcode:
            CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush()
            return False
        body = {
            'userName': name,
            'mailNo': content
        }
        params = {
            'content': body,
            'mobile': phone,
            'sex': 2,
            'tNum': 'T170701001056'
        }
        response = self.session.request(url=API_NOTIFICATION_BY_VOICE_CODE + urllib.parse.urlencode(params),
                                        method='GET', headers={'Authorization': 'APPCODE {}'.format(appcode)})
        result = response.json()
        response_message = result.get('showapi_res_body.remark')
        if response.status_code in [400, 401, 403]:
            return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_FORBID).flush()
        if response.status_code == 200 and result.get('showapi_res_body.flag'):
            CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format(response_message)).flush()
            return True
        else:
            return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(response_message)).flush()

    def send_voice_code_of_dingxin(self, phone, name='', info={}):
        """
        发送语音验证码 ( 鼎信 )
        购买地址 https://market.aliyun.com/products/56928004/cmapi026600.html?spm=5176.2020520132.101.2.51547218rkAXxy
        :return:
        """
        appcode = Config().NOTIFICATION_API_APP_CODE
        if not appcode:
            CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush()
            return False
        data = {
            'tpl_id': 'TP1901174',
            'phone': phone,
            'param': 'name:{name},job_name:{left_station}到{arrive_station}{set_type},orderno:{orderno}'.format(
                name=name, left_station=info.get('left_station'), arrive_station=info.get('arrive_station'),
                set_type=info.get('set_type'), orderno=info.get('orderno'))
        }
        response = self.session.request(url=API_NOTIFICATION_BY_VOICE_CODE_DINGXIN, method='POST', data=data,
                                        headers={'Authorization': 'APPCODE {}'.format(appcode)})
        result = response.json()
        response_message = result.get('return_code')
        if response.status_code in [400, 401, 403]:
            return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_FORBID).flush()
        if response.status_code == 200 and result.get('return_code') == '00000':
            CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format(response_message)).flush()
            return True
        else:
            return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(response_message)).flush()

    def send_email_by_smtp(self, to, title, content):
        import smtplib
        from email.message import EmailMessage
        to = to if isinstance(to, list) else [to]
        message = EmailMessage()
        message['Subject'] = title
        message['From'] = Config().EMAIL_SENDER
        message['To'] = to
        message.set_content(content)
        try:
            server = smtplib.SMTP(Config().EMAIL_SERVER_HOST)
            server.login(Config().EMAIL_SERVER_USER, Config().EMAIL_SERVER_PASSWORD)
            server.ehlo()
            server.starttls()
            server.send_message(message)
            server.quit()
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_EMAIL_SUCCESS).flush()
        except Exception as e:
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()

    def send_dingtalk_by_webbook(self, content):
        from dingtalkchatbot.chatbot import DingtalkChatbot
        webhook = Config().DINGTALK_WEBHOOK
        dingtalk = DingtalkChatbot(webhook)
        dingtalk.send_text(msg=content, is_at_all=True)
        pass

    def send_to_telegram_bot(self, content):
        bot_api_url = Config().TELEGRAM_BOT_API_URL
        if not bot_api_url:
            return False
        data = {
            'text': content
        }
        response = self.session.request(url=bot_api_url, method='POST', data=data)
        result = response.json().get('result')
        response_status = result.get('statusCode')
        if response_status == 200:
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_TELEGRAM_SUCCESS).flush()
        else:
            response_error_message = result.get('description')
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_TELEGRAM_FAIL.format(response_error_message)).flush()

    def push_to_bark(self, content):
        bark_url = Config().BARK_PUSH_URL
        if not bark_url:
            return False

        response = self.session.request(url=bark_url + '/' + content, method='get')
        result = response.json()
        response_status = result.get('code')
        if response_status == 200:
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_BARK_SUCCESS).flush()
        else:
            response_error_message = result.get('message')
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_BARK_FAIL.format(response_error_message)).flush()

    def send_serverchan(self, skey, title, content):
        from lightpush import lightpush
        lgp = lightpush()
        lgp.set_single_push(key=skey)
        try:
            lgp.single_push(title, content)
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_SERVER_CHAN_SUCCESS).flush()
        except Exception as e:
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_SERVER_CHAN_FAIL.format(e)).flush()

    def send_pushbear(self, skey, title, content):
        from lightpush import lightpush
        lgp = lightpush()
        lgp.set_group_push(key=skey)
        try:
            lgp.group_push(title, content)
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_PUSH_BEAR_SUCCESS).flush()
        except Exception as e:
            CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_PUSH_BEAR_SUCCESS.format(e)).flush()
Пример #14
0
class UserJob:
    # heartbeat = 60 * 2  # 心跳保持时长
    is_alive = True
    check_interval = 5
    key = None
    user_name = ''
    password = ''
    user = None
    info = {}  # 用户信息
    last_heartbeat = None
    is_ready = False
    user_loaded = False  # 用户是否已加载成功
    passengers = []
    retry_time = 3
    login_num = 0  # 尝试登录次数

    # Init page
    global_repeat_submit_token = None
    ticket_info_for_passenger_form = None
    order_request_dto = None

    cluster = None
    lock_init_user_time = 3 * 60
    cookie = False

    def __init__(self, info):
        self.cluster = Cluster()
        self.init_data(info)

    def init_data(self, info):
        self.session = Request()
        self.session.add_response_hook(self.response_login_check)
        self.key = str(info.get('key'))
        self.user_name = info.get('user_name')
        self.password = info.get('password')

    def update_user(self):
        from py12306.user.user import User
        self.user = User()
        self.load_user()

    def run(self):
        # load user
        self.update_user()
        self.start()

    def start(self):
        """
        检测心跳
        :return:
        """
        while True and self.is_alive:
            app_available_check()
            if Config().is_slave():
                self.load_user_from_remote()
            else:
                if Config().is_master() and not self.cookie:
                    self.load_user_from_remote()  # 主节点加载一次 Cookie
                self.check_heartbeat()
            if Const.IS_TEST: return
            stay_second(self.check_interval)

    def check_heartbeat(self):
        # 心跳检测
        if self.get_last_heartbeat() and (
                time_int() -
                self.get_last_heartbeat()) < Config().USER_HEARTBEAT_INTERVAL:
            return True
        # 只有主节点才能走到这
        if self.is_first_time() or not self.check_user_is_login():
            if not self.handle_login(): return

        self.user_did_load()
        message = UserLog.MESSAGE_USER_HEARTBEAT_NORMAL.format(
            self.get_name(),
            Config().USER_HEARTBEAT_INTERVAL)
        UserLog.add_quick_log(message).flush()

    def get_last_heartbeat(self):
        if Config().is_cluster_enabled():
            return int(
                self.cluster.session.get(Cluster.KEY_USER_LAST_HEARTBEAT, 0))

        return self.last_heartbeat

    def set_last_heartbeat(self, time=None):
        time = time if time != None else time_int()
        if Config().is_cluster_enabled():
            self.cluster.session.set(Cluster.KEY_USER_LAST_HEARTBEAT, time)
        self.last_heartbeat = time

    # def init_cookies
    def is_first_time(self):
        if Config().is_cluster_enabled():
            return not self.cluster.get_user_cookie(self.key)
        return not path.exists(self.get_cookie_path())

    def handle_login(self, expire=False):
        if expire: UserLog.print_user_expired()
        self.is_ready = False
        UserLog.print_start_login(user=self)
        return self.login()

    def login(self):
        """
        获取验证码结果
        :return 权限校验码
        """
        data = {
            'username': self.user_name,
            'password': self.password,
            'appid': 'otn'
        }
        self.request_device_id()
        answer = AuthCode.get_auth_code(self.session)
        data['answer'] = answer
        response = self.session.post(API_BASE_LOGIN.get('url'), data)
        result = response.json()
        print(result)
        if result.get('result_code') == 0:  # 登录成功
            """
            login 获得 cookie uamtk
            auth/uamtk      不请求,会返回 uamtk票据内容为空
            /otn/uamauthclient 能拿到用户名
            """
            new_tk = self.auth_uamtk()
            user_name = self.auth_uamauthclient(new_tk)
            # self.init_my_12306_api()
            self.update_user_info({'user_name': user_name})
            self.login_did_success()
            return True
        elif result.get('result_code') == 2:  # 账号之内错误
            # 登录失败,用户名或密码为空
            # 密码输入错误
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message'))).flush()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get(
                        'result_message',
                        result.get(
                            'message',
                            CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR)))).flush()

        return False

    def check_user_is_login(self):
        response = self.session.get(API_USER_LOGIN_CHECK)
        is_login = response.json().get('data.is_login', False) == 'Y'
        if is_login:
            self.save_user()
            self.set_last_heartbeat()
            return self.get_user_info(
            )  # 检测应该是不会维持状态,这里再请求下个人中心看有没有用,01-10 看来应该是没用  01-22 有时拿到的状态 是已失效的再加上试试

        return is_login

    def auth_uamtk(self):
        response = self.session.post(API_AUTH_UAMTK.get('url'),
                                     {'appid': 'otn'})
        result = response.json()
        if result.get('newapptk'):
            return result.get('newapptk')
        # TODO 处理获取失败情况
        return False

    def auth_uamauthclient(self, tk):
        response = self.session.post(API_AUTH_UAMAUTHCLIENT.get('url'),
                                     {'tk': tk})
        result = response.json()
        if result.get('username'):
            return result.get('username')
        # # TODO 处理获取失败情况
        return False

    def init_my_12306_api(self):
        response = self.session.post(API_AUTH_INITMy12306Api.get('url'))
        result = response.json()
        print(result)
        print(result.get('data')['user_name'])
        # if result.get('data')['user_name']:

    #         return result.get('data')['user_name']
    #     return False

    def request_device_id(self):
        """
        获取加密后的浏览器特征 ID
        :return:
        """
        response = self.session.get(API_GET_BROWSER_DEVICE_ID)
        if response.status_code == 200:
            try:
                result = json.loads(response.text)
                self.session.cookies.update(result)
                self.session.cookies.update({
                    'RAIL_EXPIRATION':
                    '1569235953446',
                    'RAIL_DEVICEID':
                    'AZbdkCY6rk-b3J_W98mcINAH-YjQBFH1V3Km1ksO793jzhyDkQS4_u25heGonaHws5pml1x0ZB8L2pmcUct6rztKmJV0FNmDDAJD4Fe4Y_azJhhfS4JNuvBiEO_3tvYRKgicqV75RdzYSClHbZh0AyDo4iD8V5Bf',
                })
            except:
                return False

    def login_did_success(self):
        """
        用户登录成功
        :return:
        """
        self.login_num += 1
        self.welcome_user()
        self.save_user()
        self.get_user_info()
        self.set_last_heartbeat()
        self.is_ready = True

    def welcome_user(self):
        UserLog.print_welcome_user(self)
        pass

    def get_cookie_path(self):
        return Config().USER_DATA_DIR + self.user_name + '.cookie'

    def update_user_info(self, info):
        self.info = {**self.info, **info}

    def get_name(self):
        return self.info.get('user_name', '')

    def save_user(self):
        if Config().is_master():
            self.cluster.set_user_cookie(self.key, self.session.cookies)
            self.cluster.set_user_info(self.key, self.info)
        with open(self.get_cookie_path(), 'wb') as f:
            pickle.dump(self.session.cookies, f)

    def did_loaded_user(self):
        """
        恢复用户成功
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_LOADED_USER.format(self.user_name)).flush()
        if self.check_user_is_login() and self.get_user_info():
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_SUCCESS.format(
                    self.user_name)).flush()
            UserLog.print_welcome_user(self)
            self.user_did_load()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_BUT_EXPIRED).flush()
            self.set_last_heartbeat(0)

    def user_did_load(self):
        """
        用户已经加载成功
        :return:
        """
        self.is_ready = True
        if self.user_loaded: return
        self.user_loaded = True
        Event().user_loaded({'key': self.key})  # 发布通知

    def get_user_info(self):
        response = self.session.get(API_USER_INFO.get('url'))
        result = response.json()
        user_data = result.get('data.userDTO.loginUserDTO')
        # 子节点访问会导致主节点登录失效 TODO 可快考虑实时同步 cookie
        if user_data:
            self.update_user_info({
                **user_data,
                **{
                    'user_name': user_data.get('name')
                }
            })
            self.save_user()
            return True
        return False

    def load_user(self):
        if Config().is_cluster_enabled(): return
        cookie_path = self.get_cookie_path()

        if path.exists(cookie_path):
            with open(self.get_cookie_path(), 'rb') as f:
                cookie = pickle.load(f)
                self.cookie = True
                self.session.cookies.update(cookie)
                self.did_loaded_user()
                return True
        return None

    def load_user_from_remote(self):
        cookie = self.cluster.get_user_cookie(self.key)
        info = self.cluster.get_user_info(self.key)
        if Config().is_slave() and (not cookie or not info):
            while True:  # 子节点只能取
                UserLog.add_quick_log(
                    UserLog.MESSAGE_USER_COOKIE_NOT_FOUND_FROM_REMOTE.format(
                        self.user_name)).flush()
                stay_second(self.retry_time)
                return self.load_user_from_remote()
        if info: self.info = info
        if cookie:
            self.session.cookies.update(cookie)
            if not self.cookie:  # 第一次加载
                self.cookie = True
                if not Config().is_slave():
                    self.did_loaded_user()
                else:
                    self.is_ready = True  # 设置子节点用户 已准备好
                    UserLog.print_welcome_user(self)
            return True
        return False

    def check_is_ready(self):
        return self.is_ready

    def wait_for_ready(self):
        if self.is_ready: return self
        UserLog.add_quick_log(
            UserLog.MESSAGE_WAIT_USER_INIT_COMPLETE.format(
                self.retry_time)).flush()
        stay_second(self.retry_time)
        return self.wait_for_ready()

    def destroy(self):
        """
        退出用户
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_USER_BEING_DESTROY.format(self.user_name)).flush()
        self.is_alive = False

    def response_login_check(self, response, **kwargs):
        if Config().is_master() and response.json().get(
                'data.noLogin') == 'true':  # relogin
            self.handle_login(expire=True)

    def get_user_passengers(self):
        if self.passengers: return self.passengers
        response = self.session.post(API_USER_PASSENGERS)
        result = response.json()
        if result.get('data.normal_passengers'):
            self.passengers = result.get('data.normal_passengers')
            # 将乘客写入到文件
            with open(Config().USER_PASSENGERS_FILE % self.user_name,
                      'w',
                      encoding='utf-8') as f:
                f.write(
                    json.dumps(self.passengers, indent=4, ensure_ascii=False))
            return self.passengers
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format(
                    result.get('messages',
                               CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR),
                    self.retry_time)).flush()
            if Config().is_slave():
                self.load_user_from_remote()  # 加载最新 cookie
            stay_second(self.retry_time)
            return self.get_user_passengers()

    def get_passengers_by_members(self, members):
        """
        获取格式化后的乘客信息
        :param members:
        :return:
        [{
            name: '项羽',
            type: 1,
            id_card: 0000000000000000000,
            type_text: '成人',
            enc_str: 'aaaaaa'
        }]
        """
        self.get_user_passengers()
        results = []
        for member in members:
            is_member_code = is_number(member)
            if not is_member_code:
                child_check = array_dict_find_by_key_value(
                    results, 'name', member)
            if not is_member_code and child_check:
                new_member = child_check.copy()
                new_member['type'] = UserType.CHILD
                new_member['type_text'] = dict_find_key_by_value(
                    UserType.dicts, int(new_member['type']))
            else:
                if is_member_code:
                    passenger = array_dict_find_by_key_value(
                        self.passengers, 'code', member)
                else:
                    passenger = array_dict_find_by_key_value(
                        self.passengers, 'passenger_name', member)
                if not passenger:
                    UserLog.add_quick_log(
                        UserLog.MESSAGE_USER_PASSENGERS_IS_INVALID.format(
                            self.user_name, member)).flush()
                    return False
                new_member = {
                    'name':
                    passenger.get('passenger_name'),
                    'id_card':
                    passenger.get('passenger_id_no'),
                    'id_card_type':
                    passenger.get('passenger_id_type_code'),
                    'mobile':
                    passenger.get('mobile_no'),
                    'type':
                    passenger.get('passenger_type'),
                    'type_text':
                    dict_find_key_by_value(
                        UserType.dicts, int(passenger.get('passenger_type'))),
                    'enc_str':
                    passenger.get('allEncStr')
                }
            results.append(new_member)

        return results

    def request_init_dc_page(self):
        """
        请求下单页面 拿到 token
        :return:
        """
        data = {'_json_att': ''}
        response = self.session.post(API_INITDC_URL, data)
        html = response.text
        token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html)
        form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html)
        order = re.search(r'var orderRequestDTO *= *(\{.+\})', html)
        # 系统忙,请稍后重试
        if html.find('系统忙,请稍后重试') != -1:
            OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL
                                   ).flush()  # 重试无用,直接跳过
            return False
        try:
            self.global_repeat_submit_token = token.groups()[0]
            self.ticket_info_for_passenger_form = json.loads(
                form.groups()[0].replace("'", '"'))
            self.order_request_dto = json.loads(order.groups()[0].replace(
                "'", '"'))
        except:
            return False  # TODO Error

        return True
Пример #15
0
 def __init__(self):
     self.interval = init_interval_by_number(config.QUERY_INTERVAL)
     self.session = Request()
Пример #16
0
class UserJob:
    # heartbeat = 60 * 2  # 心跳保持时长
    is_alive = True
    check_interval = 5
    key = None
    user_name = ''
    password = ''
    type = 'qr'
    user = None
    info = {}  # 用户信息
    last_heartbeat = None
    is_ready = False
    user_loaded = False  # 用户是否已加载成功
    passengers = []
    retry_time = 3
    retry_count = 0
    login_num = 0  # 尝试登录次数

    # Init page
    global_repeat_submit_token = None
    ticket_info_for_passenger_form = None
    order_request_dto = None

    cluster = None
    lock_init_user_time = 3 * 60
    cookie = False

    def __init__(self, info):
        self.cluster = Cluster()
        self.init_data(info)

    def init_data(self, info):
        self.session = Request()
        self.session.add_response_hook(self.response_login_check)
        self.key = str(info.get('key'))
        self.user_name = info.get('user_name')
        self.password = info.get('password')
        self.type = info.get('type')

    def update_user(self):
        from py12306.user.user import User
        self.user = User()
        self.load_user()

    def run(self):
        # load user
        self.update_user()
        self.start()

    def start(self):
        """
        检测心跳
        :return:
        """
        while True and self.is_alive:
            app_available_check()
            if Config().is_slave():
                self.load_user_from_remote()
            else:
                if Config().is_master() and not self.cookie: self.load_user_from_remote()  # 主节点加载一次 Cookie
                self.check_heartbeat()
            if Const.IS_TEST: return
            stay_second(self.check_interval)

    def check_heartbeat(self):
        # 心跳检测
        if self.get_last_heartbeat() and (time_int() - self.get_last_heartbeat()) < Config().USER_HEARTBEAT_INTERVAL:
            return True
        # 只有主节点才能走到这
        if self.is_first_time() or not self.check_user_is_login():
            if not self.handle_login(): return

        self.user_did_load()
        message = UserLog.MESSAGE_USER_HEARTBEAT_NORMAL.format(self.get_name(), Config().USER_HEARTBEAT_INTERVAL)
        UserLog.add_quick_log(message).flush()

    def get_last_heartbeat(self):
        if Config().is_cluster_enabled():
            return int(self.cluster.session.get(Cluster.KEY_USER_LAST_HEARTBEAT, 0))

        return self.last_heartbeat

    def set_last_heartbeat(self, time=None):
        time = time if time != None else time_int()
        if Config().is_cluster_enabled():
            self.cluster.session.set(Cluster.KEY_USER_LAST_HEARTBEAT, time)
        self.last_heartbeat = time

    # def init_cookies
    def is_first_time(self):
        if Config().is_cluster_enabled():
            return not self.cluster.get_user_cookie(self.key)
        return not path.exists(self.get_cookie_path())

    def handle_login(self, expire=False):
        if expire: UserLog.print_user_expired()
        self.is_ready = False
        UserLog.print_start_login(user=self)
        if self.type == 'qr':
            return self.qr_login()
        else:
            return self.login()

    def login(self):
        """
        获取验证码结果
        :return 权限校验码
        """
        data = {
            'username': self.user_name,
            'password': self.password,
            'appid': 'otn'
        }
        answer = AuthCode.get_auth_code(self.session)
        data['answer'] = answer
        self.request_device_id()
        response = self.session.post(API_BASE_LOGIN.get('url'), data)
        result = response.json()
        if result.get('result_code') == 0:  # 登录成功
            """
            login 获得 cookie uamtk
            auth/uamtk      不请求,会返回 uamtk票据内容为空
            /otn/uamauthclient 能拿到用户名
            """
            new_tk = self.auth_uamtk()
            user_name = self.auth_uamauthclient(new_tk)
            self.update_user_info({'user_name': user_name})
            self.login_did_success()
            return True
        elif result.get('result_code') == 2:  # 账号之内错误
            # 登录失败,用户名或密码为空
            # 密码输入错误
            UserLog.add_quick_log(UserLog.MESSAGE_LOGIN_FAIL.format(result.get('result_message'))).flush()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(result.get('result_message', result.get('message',
                                                                                          CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR)))).flush()

        return False

    def qr_login(self):
        self.request_device_id()
        image_uuid, png_path = self.download_code()
        while True:
            data = {
                'RAIL_DEVICEID': self.session.cookies.get('RAIL_DEVICEID'),
                'RAIL_EXPIRATION': self.session.cookies.get('RAIL_EXPIRATION'),
                'uuid': image_uuid,
                'appid': 'otn'
            }
            response = self.session.post(API_AUTH_QRCODE_CHECK.get('url'), data)
            result = response.json()
            result_code = int(result.get('result_code'))
            if result_code == 0:
                time.sleep(2)
            elif result_code == 1:
                UserLog.add_quick_log('请确认登录').flush()
                time.sleep(2)
            elif result_code == 2:
                break
            elif result_code == 3:
                try:
                    os.remove(png_path)
                except Exception as e:
                    UserLog.add_quick_log('无法删除文件: {}'.format(e)).flush()
                image_uuid = self.download_code()
        try:
            os.remove(png_path)
        except Exception as e:
            UserLog.add_quick_log('无法删除文件: {}'.format(e)).flush()

        self.session.get(API_USER_LOGIN, allow_redirects=True)
        new_tk = self.auth_uamtk()
        user_name = self.auth_uamauthclient(new_tk)
        self.update_user_info({'user_name': user_name})
        self.session.get(API_USER_LOGIN, allow_redirects=True)
        self.login_did_success()
        return True

    def download_code(self):
        try:
            UserLog.add_quick_log(UserLog.MESSAGE_QRCODE_DOWNLOADING).flush()
            response = self.session.post(API_AUTH_QRCODE_BASE64_DOWNLOAD.get('url'), data={'appid': 'otn'})
            result = response.json()
            if result.get('result_code') == '0':
                img_bytes = base64.b64decode(result.get('image'))
                try:
                    os.mkdir(Config().USER_DATA_DIR + '/qrcode')
                except FileExistsError:
                    pass
                png_path = path.normpath(Config().USER_DATA_DIR + '/qrcode/%d.png' % time.time())
                with open(png_path, 'wb') as file:
                    file.write(img_bytes)
                    file.close()
                if os.name == 'nt':
                    os.startfile(png_path)
                else:
                    print_qrcode(png_path)
                UserLog.add_log(UserLog.MESSAGE_QRCODE_DOWNLOADED.format(png_path)).flush()
                Notification.send_email_with_qrcode(Config().EMAIL_RECEIVER, '你有新的登录二维码啦!', png_path)
                self.retry_count = 0
                return result.get('uuid'), png_path
            raise KeyError('获取二维码失败: {}'.format(result.get('result_message')))
        except Exception as e:
            UserLog.add_quick_log(
                UserLog.MESSAGE_QRCODE_FAIL.format(e, self.retry_time)).flush()
            self.retry_count = self.retry_count + 1
            if self.retry_count == 20:
                self.retry_count = 0
                try:
                    os.remove(self.get_cookie_path())
                except:
                    pass
            time.sleep(self.retry_time)
            return self.download_code()

    def check_user_is_login(self):
        response = self.session.get(API_USER_LOGIN_CHECK)
        is_login = response.json().get('data.is_login', False) == 'Y'
        if is_login:
            self.save_user()
            self.set_last_heartbeat()
            return self.get_user_info()  # 检测应该是不会维持状态,这里再请求下个人中心看有没有用,01-10 看来应该是没用  01-22 有时拿到的状态 是已失效的再加上试试

        return is_login

    def auth_uamtk(self):
        response = self.session.post(API_AUTH_UAMTK.get('url'), {'appid': 'otn'}, headers={
            'Referer': 'https://kyfw.12306.cn/otn/passport?redirect=/otn/login/userLogin',
            'Origin': 'https://kyfw.12306.cn'
        })
        result = response.json()
        if result.get('newapptk'):
            return result.get('newapptk')
        # TODO 处理获取失败情况
        return False

    def auth_uamauthclient(self, tk):
        response = self.session.post(API_AUTH_UAMAUTHCLIENT.get('url'), {'tk': tk})
        result = response.json()
        if result.get('username'):
            return result.get('username')
        # TODO 处理获取失败情况
        return False

    def request_device_id(self):
        """
        获取加密后的浏览器特征 ID
        :return:
        """
        response = self.session.get(API_GET_BROWSER_DEVICE_ID)
        if response.status_code == 200:
            try:
                result = json.loads(response.text)
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36"
                }
                self.session.headers.update(headers)
                response = self.session.get(base64.b64decode(result['id']).decode())
                if response.text.find('callbackFunction') >= 0:
                    result = response.text[18:-2]
                result = json.loads(result)
                if not Config().is_cache_rail_id_enabled():
                   self.session.cookies.update({
                       'RAIL_EXPIRATION': result.get('exp'),
                       'RAIL_DEVICEID': result.get('dfp'),
                   })
                else:
                   self.session.cookies.update({
                       'RAIL_EXPIRATION': Config().RAIL_EXPIRATION,
                       'RAIL_DEVICEID': Config().RAIL_DEVICEID,
                   })
            except:
                return False

    def login_did_success(self):
        """
        用户登录成功
        :return:
        """
        self.login_num += 1
        self.welcome_user()
        self.save_user()
        self.get_user_info()
        self.set_last_heartbeat()
        self.is_ready = True

    def welcome_user(self):
        UserLog.print_welcome_user(self)
        pass

    def get_cookie_path(self):
        return Config().USER_DATA_DIR + self.user_name + '.cookie'

    def update_user_info(self, info):
        self.info = {**self.info, **info}

    def get_name(self):
        return self.info.get('user_name', '')

    def save_user(self):
        if Config().is_master():
            self.cluster.set_user_cookie(self.key, self.session.cookies)
            self.cluster.set_user_info(self.key, self.info)
        with open(self.get_cookie_path(), 'wb') as f:
            pickle.dump(self.session.cookies, f)

    def did_loaded_user(self):
        """
        恢复用户成功
        :return:
        """
        UserLog.add_quick_log(UserLog.MESSAGE_LOADED_USER.format(self.user_name)).flush()
        if self.check_user_is_login() and self.get_user_info():
            UserLog.add_quick_log(UserLog.MESSAGE_LOADED_USER_SUCCESS.format(self.user_name)).flush()
            UserLog.print_welcome_user(self)
            self.user_did_load()
        else:
            UserLog.add_quick_log(UserLog.MESSAGE_LOADED_USER_BUT_EXPIRED).flush()
            self.set_last_heartbeat(0)

    def user_did_load(self):
        """
        用户已经加载成功
        :return:
        """
        self.is_ready = True
        if self.user_loaded: return
        self.user_loaded = True
        Event().user_loaded({'key': self.key})  # 发布通知

    def get_user_info(self):
        response = self.session.get(API_USER_INFO.get('url'))
        result = response.json()
        user_data = result.get('data.userDTO.loginUserDTO')
        # 子节点访问会导致主节点登录失效 TODO 可快考虑实时同步 cookie
        if user_data:
            self.update_user_info({**user_data, **{'user_name': user_data.get('name')}})
            self.save_user()
            return True
        return False

    def load_user(self):
        if Config().is_cluster_enabled(): return
        cookie_path = self.get_cookie_path()

        if path.exists(cookie_path):
            with open(self.get_cookie_path(), 'rb') as f:
                cookie = pickle.load(f)
                self.cookie = True
                self.session.cookies.update(cookie)
                self.did_loaded_user()
                return True
        return None

    def load_user_from_remote(self):
        cookie = self.cluster.get_user_cookie(self.key)
        info = self.cluster.get_user_info(self.key)
        if Config().is_slave() and (not cookie or not info):
            while True:  # 子节点只能取
                UserLog.add_quick_log(UserLog.MESSAGE_USER_COOKIE_NOT_FOUND_FROM_REMOTE.format(self.user_name)).flush()
                stay_second(self.retry_time)
                return self.load_user_from_remote()
        if info: self.info = info
        if cookie:
            self.session.cookies.update(cookie)
            if not self.cookie:  # 第一次加载
                self.cookie = True
                if not Config().is_slave():
                    self.did_loaded_user()
                else:
                    self.is_ready = True  # 设置子节点用户 已准备好
                    UserLog.print_welcome_user(self)
            return True
        return False

    def check_is_ready(self):
        return self.is_ready

    def wait_for_ready(self):
        if self.is_ready: return self
        UserLog.add_quick_log(UserLog.MESSAGE_WAIT_USER_INIT_COMPLETE.format(self.retry_time)).flush()
        stay_second(self.retry_time)
        return self.wait_for_ready()

    def destroy(self):
        """
        退出用户
        :return:
        """
        UserLog.add_quick_log(UserLog.MESSAGE_USER_BEING_DESTROY.format(self.user_name)).flush()
        self.is_alive = False

    def response_login_check(self, response, **kwargs):
        if Config().is_master() and response.json().get('data.noLogin') == 'true':  # relogin
            self.handle_login(expire=True)

    def get_user_passengers(self):
        if self.passengers: return self.passengers
        response = self.session.post(API_USER_PASSENGERS)
        result = response.json()
        if result.get('data.normal_passengers'):
            self.passengers = result.get('data.normal_passengers')
            # 将乘客写入到文件
            with open(Config().USER_PASSENGERS_FILE % self.user_name, 'w', encoding='utf-8') as f:
                f.write(json.dumps(self.passengers, indent=4, ensure_ascii=False))
            return self.passengers
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format(
                    result.get('messages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR), self.retry_time)).flush()
            if Config().is_slave():
                self.load_user_from_remote()  # 加载最新 cookie
            stay_second(self.retry_time)
            return self.get_user_passengers()

    def get_passengers_by_members(self, members):
        """
        获取格式化后的乘客信息
        :param members:
        :return:
        [{
            name: '项羽',
            type: 1,
            id_card: 0000000000000000000,
            type_text: '成人',
            enc_str: 'aaaaaa'
        }]
        """
        self.get_user_passengers()
        results = []
        for member in members:
            is_member_code = is_number(member)
            if not is_member_code:
                if member[0] == "*":
                    audlt = 1
                    member = member[1:]
                else:
                    audlt = 0
                child_check = array_dict_find_by_key_value(results, 'name', member)
            if not is_member_code and child_check:
                new_member = child_check.copy()
                new_member['type'] = UserType.CHILD
                new_member['type_text'] = dict_find_key_by_value(UserType.dicts, int(new_member['type']))
            else:
                if is_member_code:
                    passenger = array_dict_find_by_key_value(self.passengers, 'code', member)
                else:
                    passenger = array_dict_find_by_key_value(self.passengers, 'passenger_name', member)
                    if audlt:
                        passenger['passenger_type'] = UserType.ADULT
                if not passenger:
                    UserLog.add_quick_log(
                        UserLog.MESSAGE_USER_PASSENGERS_IS_INVALID.format(self.user_name, member)).flush()
                    return False
                new_member = {
                    'name': passenger.get('passenger_name'),
                    'id_card': passenger.get('passenger_id_no'),
                    'id_card_type': passenger.get('passenger_id_type_code'),
                    'mobile': passenger.get('mobile_no'),
                    'type': passenger.get('passenger_type'),
                    'type_text': dict_find_key_by_value(UserType.dicts, int(passenger.get('passenger_type'))),
                    'enc_str': passenger.get('allEncStr')
                }
            results.append(new_member)

        return results

    def request_init_dc_page(self):
        """
        请求下单页面 拿到 token
        :return:
        """
        data = {'_json_att': ''}
        response = self.session.post(API_INITDC_URL, data)
        html = response.text
        token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html)
        form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html)
        order = re.search(r'var orderRequestDTO *= *(\{.+\})', html)
        # 系统忙,请稍后重试
        if html.find('系统忙,请稍后重试') != -1:
            OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL).flush()  # 重试无用,直接跳过
            return False, False, html
        try:
            self.global_repeat_submit_token = token.groups()[0]
            self.ticket_info_for_passenger_form = json.loads(form.groups()[0].replace("'", '"'))
            self.order_request_dto = json.loads(order.groups()[0].replace("'", '"'))
        except:
            return False, False, html  # TODO Error

        slide_val = re.search(r"var if_check_slide_passcode.*='(\d?)'", html)
        is_slide = False
        if slide_val:
            is_slide = int(slide_val[1]) == 1
        return True, is_slide, html
Пример #17
0
class OCR:
    """
    图片识别
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def get_img_position(cls, img):
        """
        获取图像坐标
        :param img_path:
        :return:
        """
        self = cls()
        if Config().AUTO_CODE_PLATFORM == 'free':
            return self.get_image_by_free_site(img)
        return self.get_img_position_by_ruokuai(img)

    def get_img_position_by_ruokuai(self, img):
        ruokuai_account = Config().AUTO_CODE_ACCOUNT
        soft_id = '119671'
        soft_key = '6839cbaca1f942f58d2760baba5ed987'
        rc = RKClient(ruokuai_account.get('user'), ruokuai_account.get('pwd'),
                      soft_id, soft_key)
        result = rc.rk_create(img, 6113)
        if "Result" in result:
            return self.get_image_position_by_offset(list(result['Result']))
        CommonLog.print_auto_code_fail(result.get("Error", '-'))
        return None

    def get_image_position_by_offset(self, offsets):
        positions = []
        width = 75
        height = 75
        for offset in offsets:
            random_x = random.randint(-5, 5)
            random_y = random.randint(-5, 5)
            offset = int(offset)
            x = width * ((offset - 1) % 4 + 1) - width / 2 + random_x
            y = height * math.ceil(offset / 4) - height / 2 + random_y
            positions.append(int(x))
            positions.append(int(y))
        return positions

    def get_image_by_free_site(self, img):
        data = {'base64': img}
        response = self.session.post(API_FREE_CODE_QCR_API, json=data)
        result = response.json()
        if result.get('success') and result.get('check'):
            check_data = {
                'check': result.get('check'),
                'img_buf': img,
                'logon': 1,
                'type': 'D'
            }
            check_response = self.session.post(API_FREE_CODE_QCR_API_CHECK,
                                               json=check_data)
            check_result = check_response.json()
            if check_result.get('res'):
                position = check_result.get('res')
                return position.replace('(', '').replace(')', '').split(',')

        CommonLog.print_auto_code_fail(result.get("Error", '-'))
        return None
Пример #18
0
 def __init__(self):
     self.session = Request()
Пример #19
0
class UserJob:
    # heartbeat = 60 * 2  # 心跳保持时长
    is_alive = True
    check_interval = 5
    key = None
    user_name = ''
    password = ''
    user = None
    info = {}  # 用户信息
    last_heartbeat = None
    is_ready = False
    user_loaded = False  # 用户是否已加载成功
    passengers = []
    retry_time = 3
    login_num = 0  # 尝试登录次数

    # Init page
    global_repeat_submit_token = None
    ticket_info_for_passenger_form = None
    order_request_dto = None

    cluster = None
    lock_init_user_time = 3 * 60
    cookie = False

    def __init__(self, info):
        self.cluster = Cluster()
        self.init_data(info)

    def init_data(self, info):
        self.session = Request()
        self.session.add_response_hook(self.response_login_check)
        self.key = str(info.get('key'))
        self.user_name = info.get('user_name')
        self.password = info.get('password')

    def update_user(self):
        from py12306.user.user import User
        self.user = User()
        self.load_user()

    def run(self):
        # load user
        self.update_user()
        self.start()

    def start(self):
        """
        检测心跳
        :return:
        """
        while True and self.is_alive:
            app_available_check()
            if Config().is_slave():
                self.load_user_from_remote()
            else:
                if Config().is_master() and not self.cookie:
                    self.load_user_from_remote()  # 主节点加载一次 Cookie
                self.check_heartbeat()
            if Const.IS_TEST: return
            stay_second(self.check_interval)

    def check_heartbeat(self):
        # 心跳检测
        if self.get_last_heartbeat() and (
                time_int() -
                self.get_last_heartbeat()) < Config().USER_HEARTBEAT_INTERVAL:
            return True
        # 只有主节点才能走到这
        if self.is_first_time() or not self.check_user_is_login():
            if not self.handle_login(): return

        self.user_did_load()
        message = UserLog.MESSAGE_USER_HEARTBEAT_NORMAL.format(
            self.get_name(),
            Config().USER_HEARTBEAT_INTERVAL)
        UserLog.add_quick_log(message).flush()

    def get_last_heartbeat(self):
        if Config().is_cluster_enabled():
            return int(
                self.cluster.session.get(Cluster.KEY_USER_LAST_HEARTBEAT, 0))

        return self.last_heartbeat

    def set_last_heartbeat(self, time=None):
        time = time if time != None else time_int()
        if Config().is_cluster_enabled():
            self.cluster.session.set(Cluster.KEY_USER_LAST_HEARTBEAT, time)
        self.last_heartbeat = time

    # def init_cookies
    def is_first_time(self):
        if Config().is_cluster_enabled():
            return not self.cluster.get_user_cookie(self.key)
        return not path.exists(self.get_cookie_path())

    def handle_login(self, expire=False):
        if expire: UserLog.print_user_expired()
        self.is_ready = False
        UserLog.print_start_login(user=self)
        return self.login()

    def login(self):
        """
        获取验证码结果
        :return 权限校验码
        """
        data = {
            'username': self.user_name,
            'password': self.password,
            'appid': 'otn'
        }
        self.request_device_id()
        answer = AuthCode.get_auth_code(self.session)
        data['answer'] = answer
        response = self.session.post(API_BASE_LOGIN.get('url'), data)
        result = response.json()
        if result.get('result_code') == 0:  # 登录成功
            """
            login 获得 cookie uamtk
            auth/uamtk      不请求,会返回 uamtk票据内容为空
            /otn/uamauthclient 能拿到用户名
            """
            new_tk = self.auth_uamtk()
            user_name = self.auth_uamauthclient(new_tk)
            self.update_user_info({'user_name': user_name})
            self.login_did_success()
            return True
        elif result.get('result_code') == 2:  # 账号之内错误
            # 登录失败,用户名或密码为空
            # 密码输入错误
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message'))).flush()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get(
                        'result_message',
                        result.get(
                            'message',
                            CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR)))).flush()

        return False

    def check_user_is_login(self):
        response = self.session.get(API_USER_LOGIN_CHECK)
        is_login = response.json().get('data.is_login', False) == 'Y'
        if is_login:
            self.save_user()
            self.set_last_heartbeat()
            return self.get_user_info(
            )  # 检测应该是不会维持状态,这里再请求下个人中心看有没有用,01-10 看来应该是没用  01-22 有时拿到的状态 是已失效的再加上试试

        return is_login

    def auth_uamtk(self):
        response = self.session.post(API_AUTH_UAMTK.get('url'),
                                     {'appid': 'otn'})
        result = response.json()
        if result.get('newapptk'):
            return result.get('newapptk')
        # TODO 处理获取失败情况
        return False

    def auth_uamauthclient(self, tk):
        response = self.session.post(API_AUTH_UAMAUTHCLIENT.get('url'),
                                     {'tk': tk})
        result = response.json()
        if result.get('username'):
            return result.get('username')
        # TODO 处理获取失败情况
        return False

    def request_device_id(self):
        """
        获取加密后的浏览器特征 ID
        :return:
        """
        """
        TODO 通过程序进行加密,目前不清楚固定参数能使用多久
        encrypt js address: https://kyfw.12306.cn/otn/HttpZF/GetJS   line:2716
        """
        params = {
            "algID": "ozy7Gbfya4",
            "hashCode": "SPPnxwJaxslzp6PLz38mV_078n44WjSOp7vTgvQpGxA",
            "FMQw": "0",
            "q4f3": "zh-CN",
            "VySQ": "FGGlO7IzXMj0sfYT705RPKxtnYIHB5MI",
            "VPIf": "1",
            "custID": "133",
            "VEek": "unknown",
            "dzuS": "0",
            "yD16": "0",
            "EOQP": "c227b88b01f5c513710d4b9f16a5ce52",
            "lEnu": "167838050",
            "jp76": "52d67b2a5aa5e031084733d5006cc664",
            "hAqN": "MacIntel",
            "platform": "WEB",
            "ks0Q": "d22ca0b81584fbea62237b14bd04c866",
            "TeRS": "878x1440",
            "tOHY": "24xx900x1440",
            "Fvje": "i1l1o1s1",
            "q5aJ": "-8",
            "wNLf": "99115dfb07133750ba677d055874de87",
            "0aew":
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
            "E3gR": "8b9855470c623dc9bd6e495a1417f6f8",
            "timestamp": int(time.time() * 1000)
        }
        response = self.session.get(API_GET_BROWSER_DEVICE_ID, params=params)
        if response.text.find('callbackFunction') >= 0:
            result = response.text[18:-2]
            try:
                result = json.loads(result)
                self.session.cookies.update({
                    'RAIL_EXPIRATION':
                    result.get('exp'),
                    'RAIL_DEVICEID':
                    result.get('dfp'),
                })
            except:
                return False

    def login_did_success(self):
        """
        用户登录成功
        :return:
        """
        self.login_num += 1
        self.welcome_user()
        self.save_user()
        self.get_user_info()
        self.set_last_heartbeat()
        self.is_ready = True

    def welcome_user(self):
        UserLog.print_welcome_user(self)
        pass

    def get_cookie_path(self):
        return Config().USER_DATA_DIR + self.user_name + '.cookie'

    def update_user_info(self, info):
        self.info = {**self.info, **info}

    def get_name(self):
        return self.info.get('user_name', '')

    def save_user(self):
        if Config().is_master():
            self.cluster.set_user_cookie(self.key, self.session.cookies)
            self.cluster.set_user_info(self.key, self.info)
        with open(self.get_cookie_path(), 'wb') as f:
            pickle.dump(self.session.cookies, f)

    def did_loaded_user(self):
        """
        恢复用户成功
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_LOADED_USER.format(self.user_name)).flush()
        if self.check_user_is_login() and self.get_user_info():
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_SUCCESS.format(
                    self.user_name)).flush()
            UserLog.print_welcome_user(self)
            self.user_did_load()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_BUT_EXPIRED).flush()
            self.set_last_heartbeat(0)

    def user_did_load(self):
        """
        用户已经加载成功
        :return:
        """
        self.is_ready = True
        if self.user_loaded: return
        self.user_loaded = True
        Event().user_loaded({'key': self.key})  # 发布通知

    def get_user_info(self):
        response = self.session.get(API_USER_INFO.get('url'))
        result = response.json()
        user_data = result.get('data.userDTO.loginUserDTO')
        # 子节点访问会导致主节点登录失效 TODO 可快考虑实时同步 cookie
        if user_data:
            self.update_user_info({
                **user_data,
                **{
                    'user_name': user_data.get('name')
                }
            })
            self.save_user()
            return True
        return False

    def load_user(self):
        if Config().is_cluster_enabled(): return
        cookie_path = self.get_cookie_path()

        if path.exists(cookie_path):
            with open(self.get_cookie_path(), 'rb') as f:
                cookie = pickle.load(f)
                self.cookie = True
                self.session.cookies.update(cookie)
                self.did_loaded_user()
                return True
        return None

    def load_user_from_remote(self):
        cookie = self.cluster.get_user_cookie(self.key)
        info = self.cluster.get_user_info(self.key)
        if Config().is_slave() and (not cookie or not info):
            while True:  # 子节点只能取
                UserLog.add_quick_log(
                    UserLog.MESSAGE_USER_COOKIE_NOT_FOUND_FROM_REMOTE.format(
                        self.user_name)).flush()
                stay_second(self.retry_time)
                return self.load_user_from_remote()
        if info: self.info = info
        if cookie:
            self.session.cookies.update(cookie)
            if not self.cookie:  # 第一次加载
                self.cookie = True
                if not Config().is_slave():
                    self.did_loaded_user()
                else:
                    self.is_ready = True  # 设置子节点用户 已准备好
                    UserLog.print_welcome_user(self)
            return True
        return False

    def check_is_ready(self):
        return self.is_ready

    def wait_for_ready(self):
        if self.is_ready: return self
        UserLog.add_quick_log(
            UserLog.MESSAGE_WAIT_USER_INIT_COMPLETE.format(
                self.retry_time)).flush()
        stay_second(self.retry_time)
        return self.wait_for_ready()

    def destroy(self):
        """
        退出用户
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_USER_BEING_DESTROY.format(self.user_name)).flush()
        self.is_alive = False

    def response_login_check(self, response, **kwargs):
        if Config().is_master() and response.json().get(
                'data.noLogin') == 'true':  # relogin
            self.handle_login(expire=True)

    def get_user_passengers(self):
        if self.passengers: return self.passengers
        response = self.session.post(API_USER_PASSENGERS)
        result = response.json()
        if result.get('data.normal_passengers'):
            self.passengers = result.get('data.normal_passengers')
            # 将乘客写入到文件
            with open(Config().USER_PASSENGERS_FILE % self.user_name,
                      'w',
                      encoding='utf-8') as f:
                f.write(
                    json.dumps(self.passengers, indent=4, ensure_ascii=False))
            return self.passengers
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format(
                    result.get('messages',
                               CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR),
                    self.retry_time)).flush()
            if Config().is_slave():
                self.load_user_from_remote()  # 加载最新 cookie
            stay_second(self.retry_time)
            return self.get_user_passengers()

    def get_passengers_by_members(self, members):
        """
        获取格式化后的乘客信息
        :param members:
        :return:
        [{
            name: '项羽',
            type: 1,
            id_card: 0000000000000000000,
            type_text: '成人'
        }]
        """
        self.get_user_passengers()
        results = []
        for member in members:
            is_member_code = is_number(member)
            if not is_member_code:
                child_check = array_dict_find_by_key_value(
                    results, 'name', member)
            if not is_member_code and child_check:
                new_member = child_check.copy()
                new_member['type'] = UserType.CHILD
                new_member['type_text'] = dict_find_key_by_value(
                    UserType.dicts, int(new_member['type']))
            else:
                if is_member_code:
                    passenger = array_dict_find_by_key_value(
                        self.passengers, 'code', member)
                else:
                    passenger = array_dict_find_by_key_value(
                        self.passengers, 'passenger_name', member)
                if not passenger:
                    UserLog.add_quick_log(
                        UserLog.MESSAGE_USER_PASSENGERS_IS_INVALID.format(
                            self.user_name, member)).flush()
                    return False
                new_member = {
                    'name':
                    passenger.get('passenger_name'),
                    'id_card':
                    passenger.get('passenger_id_no'),
                    'id_card_type':
                    passenger.get('passenger_id_type_code'),
                    'mobile':
                    passenger.get('mobile_no'),
                    'type':
                    passenger.get('passenger_type'),
                    'type_text':
                    dict_find_key_by_value(
                        UserType.dicts, int(passenger.get('passenger_type')))
                }
            results.append(new_member)

        return results

    def request_init_dc_page(self):
        """
        请求下单页面 拿到 token
        :return:
        """
        data = {'_json_att': ''}
        response = self.session.post(API_INITDC_URL, data)
        html = response.text
        token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html)
        form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html)
        order = re.search(r'var orderRequestDTO *= *(\{.+\})', html)
        # 系统忙,请稍后重试
        if html.find('系统忙,请稍后重试') != -1:
            OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL
                                   ).flush()  # 重试无用,直接跳过
            return False
        try:
            self.global_repeat_submit_token = token.groups()[0]
            self.ticket_info_for_passenger_form = json.loads(
                form.groups()[0].replace("'", '"'))
            self.order_request_dto = json.loads(order.groups()[0].replace(
                "'", '"'))
        except:
            pass  # TODO Error

        return True
Пример #20
0
class Notification():
    """
    通知类
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def voice_code(cls, phone, name='', content=''):
        self = cls()
        self.send_voice_code_of_yiyuan(phone, name=name, content=content)

    @classmethod
    def send_email(cls, to, title='', content=''):
        self = cls()
        self.send_email_by_smtp(to, title, content)

    def send_voice_code_of_yiyuan(self, phone, name='', content=''):
        """
        发送语音验证码
        购买地址 https://market.aliyun.com/products/57126001/cmapi019902.html?spm=5176.2020520132.101.5.37857218O6iJ3n
        :return:
        """
        appcode = Config().NOTIFICATION_API_APP_CODE
        if not appcode:
            CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush()
            return False
        body = {'userName': name, 'mailNo': content}
        params = {
            'content': body,
            'mobile': phone,
            'sex': 2,
            'tNum': 'T170701001056'
        }
        response = self.session.request(
            url=API_NOTIFICATION_BY_VOICE_CODE +
            urllib.parse.urlencode(params),
            method='GET',
            headers={'Authorization': 'APPCODE {}'.format(appcode)})
        result = response.json()
        response_message = result.get('showapi_res_body.remark')
        if response.status_code in [400, 401, 403]:
            return CommonLog.add_quick_log(
                CommonLog.MESSAGE_VOICE_API_FORBID).flush()
        if response.status_code == 200 and result.get('showapi_res_body.flag'):
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format(
                    response_message)).flush()
            return True
        else:
            return CommonLog.add_quick_log(
                CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(
                    response_message)).flush()

    def send_email_by_smtp(self, to, title, content):
        import smtplib
        from email.message import EmailMessage
        to = to if isinstance(to, list) else [to]
        message = EmailMessage()
        message['Subject'] = title
        message['From'] = '*****@*****.**'
        message['To'] = to
        message.set_content(content)
        try:
            server = smtplib.SMTP(Config().EMAIL_SERVER_HOST)
            server.login(Config().EMAIL_SERVER_USER,
                         Config().EMAIL_SERVER_PASSWORD)
            server.send_message(message)
            server.quit()
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_SEND_EMAIL_SUCCESS).flush()
        except Exception as e:
            CommonLog.add_quick_log(
                CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()
Пример #21
0
class UserJob:
    # heartbeat = 60 * 2  # 心跳保持时长
    is_alive = True
    check_interval = 5
    key = None
    user_name = ''
    password = ''
    user = None
    info = {}  # 用户信息
    last_heartbeat = None
    is_ready = False
    user_loaded = False  # 用户是否已加载成功
    passengers = []
    retry_time = 3
    login_num = 0  # 尝试登录次数

    # Init page
    global_repeat_submit_token = None
    ticket_info_for_passenger_form = None
    order_request_dto = None

    cluster = None
    lock_init_user_time = 3 * 60
    cookie = False

    def __init__(self, info):
        self.cluster = Cluster()
        self.init_data(info)

    def init_data(self, info):
        self.session = Request()
        self.key = str(info.get('key'))
        self.user_name = info.get('user_name')
        self.password = info.get('password')

    def update_user(self):
        from py12306.user.user import User
        self.user = User()
        # if not Const.IS_TEST:  测试模块下也可以从文件中加载用户
        self.load_user()

    def run(self):
        # load user
        self.update_user()
        self.start()

    def start(self):
        """
        检测心跳
        :return:
        """
        while True and self.is_alive:
            app_available_check()
            if Config().is_slave():
                self.load_user_from_remote()
                pass
            else:
                if Config().is_master() and not self.cookie:
                    self.load_user_from_remote()  # 主节点加载一次 Cookie
                self.check_heartbeat()
            if Const.IS_TEST: return
            stay_second(self.check_interval)

    def check_heartbeat(self):
        # 心跳检测
        if self.get_last_heartbeat() and (
                time_int() -
                self.get_last_heartbeat()) < Config().USER_HEARTBEAT_INTERVAL:
            return True
        # 只有主节点才能走到这
        if self.is_first_time() or not self.check_user_is_login():
            self.is_ready = False
            if not self.handle_login(): return
            self.set_last_heartbeat()

        self.is_ready = True
        self.user_did_load()
        message = UserLog.MESSAGE_USER_HEARTBEAT_NORMAL.format(
            self.get_name(),
            Config().USER_HEARTBEAT_INTERVAL)
        if not Config.is_cluster_enabled():
            UserLog.add_quick_log(message).flush()
        else:
            self.cluster.publish_log_message(message)
        # self.set_last_heartbeat()

    def get_last_heartbeat(self):
        if Config().is_cluster_enabled():
            return int(
                self.cluster.session.get(Cluster.KEY_USER_LAST_HEARTBEAT, 0))

        return self.last_heartbeat

    def set_last_heartbeat(self, time=None):
        if Config().is_cluster_enabled():
            return self.cluster.session.set(
                Cluster.KEY_USER_LAST_HEARTBEAT,
                time if time != None else time_int())
        self.last_heartbeat = time_int()

    # def init_cookies
    def is_first_time(self):
        if Config().is_cluster_enabled():
            return not self.cluster.get_user_cookie(self.key)
        return not path.exists(self.get_cookie_path())

    def handle_login(self):
        UserLog.print_start_login(user=self)
        return self.login()

    def login(self):
        """
        获取验证码结果
        :return 权限校验码
        """
        data = {
            'username': self.user_name,
            'password': self.password,
            'appid': 'otn'
        }
        answer = AuthCode.get_auth_code(self.session)
        data['answer'] = answer
        response = self.session.post(API_BASE_LOGIN.get('url'), data)
        result = response.json()
        if result.get('result_code') == 0:  # 登录成功
            """
            login 获得 cookie uamtk
            auth/uamtk      不请求,会返回 uamtk票据内容为空
            /otn/uamauthclient 能拿到用户名
            """
            new_tk = self.auth_uamtk()
            user_name = self.auth_uamauthclient(new_tk)
            self.update_user_info({'user_name': user_name})
            self.login_did_success()
            return True
        elif result.get('result_code') == 2:  # 账号之内错误
            # 登录失败,用户名或密码为空
            # 密码输入错误
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message'))).flush()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOGIN_FAIL.format(
                    result.get('result_message', result.get('message',
                                                            '-')))).flush()

        return False

        pass

    def check_user_is_login(self):
        response = self.session.get(API_USER_CHECK.get('url'))
        is_login = response.json().get('data.flag', False)
        if is_login:
            self.save_user()
            self.set_last_heartbeat()
            # self.get_user_info()  # 检测应该是不会维持状态,这里再请求下个人中心看有没有用,01-10 看来应该是没用

        return is_login

    def auth_uamtk(self):
        response = self.session.post(API_AUTH_UAMTK.get('url'),
                                     {'appid': 'otn'})
        result = response.json()
        if result.get('newapptk'):
            return result.get('newapptk')
        # TODO 处理获取失败情况
        return False

    def auth_uamauthclient(self, tk):
        response = self.session.post(API_AUTH_UAMAUTHCLIENT.get('url'),
                                     {'tk': tk})
        result = response.json()
        if result.get('username'):
            return result.get('username')
        # TODO 处理获取失败情况
        return False

    def login_did_success(self):
        """
        用户登录成功
        :return:
        """
        self.login_num += 1
        self.welcome_user()
        self.save_user()
        self.get_user_info()
        pass

    def welcome_user(self):
        UserLog.print_welcome_user(self)
        pass

    def get_cookie_path(self):
        return Config().USER_DATA_DIR + self.user_name + '.cookie'

    def update_user_info(self, info):
        self.info = {**self.info, **info}

    def get_name(self):
        return self.info.get('user_name', '')

    def save_user(self):
        if Config().is_master():
            self.cluster.set_user_cookie(self.key, self.session.cookies)
            self.cluster.set_user_info(self.key, self.info)
        with open(self.get_cookie_path(), 'wb') as f:
            pickle.dump(self.session.cookies, f)

    def did_loaded_user(self):
        """
        恢复用户成功
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_LOADED_USER.format(self.user_name)).flush()
        if self.check_user_is_login() and self.get_user_info():
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_SUCCESS.format(
                    self.user_name)).flush()
            self.is_ready = True
            UserLog.print_welcome_user(self)
            self.user_did_load()
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_LOADED_USER_BUT_EXPIRED).flush()
            self.set_last_heartbeat(0)

    def user_did_load(self):
        """
        用户已经加载成功
        :return:
        """
        if self.user_loaded: return
        self.user_loaded = True
        Event().user_loaded({'key': self.key})  # 发布通知

    def get_user_info(self):
        response = self.session.get(API_USER_INFO.get('url'))
        result = response.json()
        user_data = result.get('data.userDTO.loginUserDTO')
        # 子节点访问会导致主节点登录失效 TODO 可快考虑实时同步 cookie
        if user_data:
            self.update_user_info({
                **user_data,
                **{
                    'user_name': user_data.get('name')
                }
            })
            self.save_user()
            return True
        return None

    def load_user(self):
        if Config().is_cluster_enabled(): return
        cookie_path = self.get_cookie_path()

        if path.exists(cookie_path):
            with open(self.get_cookie_path(), 'rb') as f:
                cookie = pickle.load(f)
                self.cookie = True
                self.session.cookies.update(cookie)
                self.did_loaded_user()
                return True
        return None

    def load_user_from_remote(self):
        cookie = self.cluster.get_user_cookie(self.key)
        info = self.cluster.get_user_info(self.key)
        if Config().is_slave() and (not cookie or not info):
            while True:  # 子节点只能取
                UserLog.add_quick_log(
                    UserLog.MESSAGE_USER_COOKIE_NOT_FOUND_FROM_REMOTE.format(
                        self.user_name)).flush()
                stay_second(self.retry_time)
                return self.load_user_from_remote()
        if info: self.info = info
        if cookie:
            self.session.cookies.update(cookie)
            if not self.cookie:  # 第一次加载
                self.cookie = True
                if not Config().is_slave():
                    self.did_loaded_user()
                else:
                    UserLog.print_welcome_user(self)
            return True
        return False

    def check_is_ready(self):
        return self.is_ready

    def wait_for_ready(self):
        if self.is_ready: return self
        UserLog.add_quick_log(
            UserLog.MESSAGE_WAIT_USER_INIT_COMPLETE.format(
                self.retry_time)).flush()
        stay_second(self.retry_time)
        return self.wait_for_ready()

    def destroy(self):
        """
        退出用户
        :return:
        """
        UserLog.add_quick_log(
            UserLog.MESSAGE_USER_BEING_DESTROY.format(self.user_name)).flush()
        self.is_alive = False

    def get_user_passengers(self):
        if self.passengers: return self.passengers
        response = self.session.post(API_USER_PASSENGERS)
        result = response.json()
        if result.get('data.normal_passengers'):
            self.passengers = result.get('data.normal_passengers')
            return self.passengers
        else:
            UserLog.add_quick_log(
                UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format(
                    result.get('messages', '-'), self.retry_time)).flush()
            stay_second(self.retry_time)
            return self.get_user_passengers()

    def get_passengers_by_members(self, members):
        """
        获取格式化后的乘客信息
        :param members:
        :return:
        [{
            name: '项羽',
            type: 1,
            id_card: 0000000000000000000,
            type_text: '成人'
        }]
        """
        self.get_user_passengers()
        results = []
        for member in members:
            child_check = array_dict_find_by_key_value(results, 'name', member)
            if child_check:
                new_member = child_check.copy()
                new_member['type'] = UserType.CHILD
                new_member['type_text'] = dict_find_key_by_value(
                    UserType.dicts, int(new_member['type']))
            else:
                passenger = array_dict_find_by_key_value(
                    self.passengers, 'passenger_name', member)
                if not passenger:
                    UserLog.add_quick_log(
                        UserLog.MESSAGE_USER_PASSENGERS_IS_INVALID.format(
                            self.user_name, member)).flush()
                    return False
                new_member = {
                    'name':
                    passenger.get('passenger_name'),
                    'id_card':
                    passenger.get('passenger_id_no'),
                    'id_card_type':
                    passenger.get('passenger_id_type_code'),
                    'mobile':
                    passenger.get('mobile_no'),
                    'type':
                    passenger.get('passenger_type'),
                    'type_text':
                    dict_find_key_by_value(
                        UserType.dicts, int(passenger.get('passenger_type')))
                }
            results.append(new_member)

        return results

    def request_init_dc_page(self):
        """
        请求下单页面 拿到 token
        :return:
        """
        data = {'_json_att': ''}
        response = self.session.post(API_INITDC_URL, data)
        html = response.text
        token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html)
        form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html)
        order = re.search(r'var orderRequestDTO *= *(\{.+\})', html)
        # 系统忙,请稍后重试
        if html.find('系统忙,请稍后重试') != -1:
            OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL
                                   ).flush()  # 重试无用,直接跳过
            return False
        try:
            self.global_repeat_submit_token = token.groups()[0]
            self.ticket_info_for_passenger_form = json.loads(
                form.groups()[0].replace("'", '"'))
            self.order_request_dto = json.loads(order.groups()[0].replace(
                "'", '"'))
        except:
            pass  # TODO Error

        return True
Пример #22
0
class Query:
    """
    余票查询

    """
    jobs = []
    query_jobs = []
    # session = {}

    # 查询间隔
    interval = {}
    cluster = None

    is_in_thread = False
    retry_time = 3
    is_ready = False
    api_type = None  # Query api url, Current know value  leftTicket/queryX | leftTicket/queryZ

    def __init__(self):
        self.session = Request()
        self.cluster = Cluster()
        self.update_query_interval()
        self.update_query_jobs()
        self.get_query_api_type()

    def update_query_interval(self, auto=False):
        self.interval = init_interval_by_number(Config().QUERY_INTERVAL)
        if auto:
            jobs_do(self.jobs, 'update_interval')

    def update_query_jobs(self, auto=False):
        self.query_jobs = Config().QUERY_JOBS
        if auto:
            QueryLog.add_quick_log(QueryLog.MESSAGE_JOBS_DID_CHANGED).flush()
            self.refresh_jobs()
            if not Config().is_slave():
                jobs_do(self.jobs, 'check_passengers')

    @classmethod
    def run(cls):
        self = cls()
        app_available_check()
        self.start()
        pass

    @classmethod
    def check_before_run(cls):
        self = cls()
        self.init_jobs()
        self.is_ready = True

    def start(self):
        # return # DEBUG
        QueryLog.init_data()
        stay_second(3)
        # 多线程
        while True:
            if Config().QUERY_JOB_THREAD_ENABLED:  # 多线程
                if not self.is_in_thread:
                    self.is_in_thread = True
                    create_thread_and_run(jobs=self.jobs, callback_name='run', wait=Const.IS_TEST)
                if Const.IS_TEST: return
                stay_second(self.retry_time)
            else:
                if not self.jobs: break
                self.is_in_thread = False
                jobs_do(self.jobs, 'run')
                if Const.IS_TEST: return

        # while True:
        #     app_available_check()
        #     if Config().QUERY_JOB_THREAD_ENABLED:  # 多线程
        #         create_thread_and_run(jobs=self.jobs, callback_name='run')
        #     else:
        #         for job in self.jobs: job.run()
        #     if Const.IS_TEST: return
        # self.refresh_jobs()  # 刷新任务

    def refresh_jobs(self):
        """
        更新任务
        :return:
        """
        allow_jobs = []
        for job in self.query_jobs:
            id = md5(job)
            job_ins = objects_find_object_by_key_value(self.jobs, 'id', id)  # [1 ,2]
            if not job_ins:
                job_ins = self.init_job(job)
                if Config().QUERY_JOB_THREAD_ENABLED:  # 多线程重新添加
                    create_thread_and_run(jobs=job_ins, callback_name='run', wait=Const.IS_TEST)
            allow_jobs.append(job_ins)

        for job in self.jobs:  # 退出已删除 Job
            if job not in allow_jobs: job.destroy()

        QueryLog.print_init_jobs(jobs=self.jobs)

    def init_jobs(self):
        for job in self.query_jobs:
            self.init_job(job)
        QueryLog.print_init_jobs(jobs=self.jobs)

    def init_job(self, job):
        job = Job(info=job, query=self)
        self.jobs.append(job)
        return job

    @classmethod
    def wait_for_ready(cls):
        self = cls()
        if self.is_ready: return self
        stay_second(self.retry_time)
        return self.wait_for_ready()

    @classmethod
    def job_by_name(cls, name) -> Job:
        self = cls()
        for job in self.jobs:
            if job.job_name == name: return job
        return None

    @classmethod
    def job_by_name(cls, name) -> Job:
        self = cls()
        return objects_find_object_by_key_value(self.jobs, 'job_name', name)

    @classmethod
    def job_by_account_key(cls, account_key) -> Job:
        self = cls()
        return objects_find_object_by_key_value(self.jobs, 'account_key', account_key)

    @classmethod
    def get_query_api_type(cls):
        import re
        self = cls()
        if self.api_type:
            return self.api_type
        response = self.session.get(API_QUERY_INIT_PAGE)
        if response.status_code == 200:
            res = re.search(r'var CLeftTicketUrl = \'(.*)\';', response.text)
            try:
                self.api_type = res.group(1)
            except IndexError:
                pass
        return cls.get_query_api_type()
Пример #23
0
class OCR:
    """
    图片识别
    """
    session = None

    def __init__(self):
        self.session = Request()

    @classmethod
    def get_img_position(cls, img):
        """
        获取图像坐标
        :param img_path:
        :return:
        """
        self = cls()
        if Config().AUTO_CODE_PLATFORM == 'free_1':
            return self.get_image_by_free_site(img)
        elif Config().AUTO_CODE_PLATFORM == 'free_2':
            return self.get_image_by_local_verify(img)
        return self.get_img_position_by_ruokuai(img)

    def get_img_position_by_ruokuai(self, img):
        ruokuai_account = Config().AUTO_CODE_ACCOUNT
        soft_id = '119671'
        soft_key = '6839cbaca1f942f58d2760baba5ed987'
        rc = RKClient(ruokuai_account.get('user'), ruokuai_account.get('pwd'),
                      soft_id, soft_key)
        result = rc.rk_create(img, 6113)
        if "Result" in result:
            return self.get_image_position_by_offset(list(result['Result']))
        CommonLog.print_auto_code_fail(
            result.get("Error", CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))
        return None

    def get_image_position_by_offset(self, offsets):
        positions = []
        width = 75
        height = 75
        for offset in offsets:
            random_x = random.randint(-5, 5)
            random_y = random.randint(-5, 5)
            offset = int(offset)
            x = width * ((offset - 1) % 4 + 1) - width / 2 + random_x
            y = height * math.ceil(offset / 4) - height / 2 + random_y
            positions.append(int(x))
            positions.append(int(y))
        return positions

    def get_image_by_free_site(self, img):
        data = {'img': img}
        response = self.session.post(API_FREE_CODE_QCR_API, data=data)
        result = response.json()
        if result.get('msg') == 'success':
            pos = result.get('result')
            return self.get_image_position_by_offset(pos)

        CommonLog.print_auto_code_fail(
            CommonLog.MESSAGE_GET_RESPONSE_FROM_FREE_AUTO_CODE)
        return None

    def get_image_by_local_verify(self, img):
        with open('./tkcode.png', 'rb') as f:
            result = f.read()
        result = verify(result)
        print(result)
        return self.codexy(Ofset=result, is_raw_input=False)

    def codexy(self, Ofset=None, is_raw_input=True):
        """
        获取验证码
        :return: str
        """
        if is_raw_input:
            print(u"""
                *****************
                | 1 | 2 | 3 | 4 |
                *****************
                | 5 | 6 | 7 | 8 |
                *****************
                """)
            print(
                u"验证码分为8个,对应上面数字,例如第一和第二张,输入1, 2  如果开启cdn查询的话,会冲掉提示,直接鼠标点击命令行获取焦点,输入即可,不要输入空格"
            )
            print(u"如果是linux无图形界面,请使用自动打码,is_auto_code: True")
            print(u"如果没有弹出验证码,请手动双击根目录下的tkcode.png文件")
            Ofset = input(u"输入对应的验证码: ")
        if isinstance(Ofset, list):
            select = Ofset
        else:
            Ofset = Ofset.replace(",", ",")
            select = Ofset.split(',')
        post = []
        offsetsX = 0  # 选择的答案的left值,通过浏览器点击8个小图的中点得到的,这样基本没问题
        offsetsY = 0  # 选择的答案的top值
        for ofset in select:
            if ofset == '1':
                offsetsY = 77
                offsetsX = 40
            elif ofset == '2':
                offsetsY = 77
                offsetsX = 112
            elif ofset == '3':
                offsetsY = 77
                offsetsX = 184
            elif ofset == '4':
                offsetsY = 77
                offsetsX = 256
            elif ofset == '5':
                offsetsY = 149
                offsetsX = 40
            elif ofset == '6':
                offsetsY = 149
                offsetsX = 112
            elif ofset == '7':
                offsetsY = 149
                offsetsX = 184
            elif ofset == '8':
                offsetsY = 149
                offsetsX = 256
            else:
                pass
            post.append(offsetsX)
            post.append(offsetsY)
        # randCode = str(post).replace(']', '').replace('[', '').replace("'", '').replace(' ', '')
        print(u"验证码识别坐标为{0}".format(post))
        # return randCode
        return post