'''
Function:
    微博模拟登录
Author:
    Charles
微信公众号:
    Charles的皮卡丘
更新日期:
    2021-12-25
'''
import os
import re
import time
import json
import requests
import warnings
from ..utils.misc import *
warnings.filterwarnings('ignore')


'''PC端登录微博'''
class weiboPC():
    is_callable = True
    def __init__(self, **kwargs):
        for key, value in kwargs.items(): setattr(self, key, value)
        self.info = 'login in weibo in pc mode'
        self.session = requests.Session()
        self.__initialize()
    '''登录函数'''
    def login(self, username, password, crack_captcha_func=None, **kwargs):
        # 设置代理
        self.session.proxies.update(kwargs.get('proxies', {}))
        # 模拟登录
        data = {
            'username': username,
            'password': password,
            'savestate': '1',
            'ec': '1',
            'pagerefer': '',
            'entry': 'wapsso',
            'sinacnlogin': '1',
        }
        response = self.session.post(self.login_url, headers=self.login_headers, data=data)
        response_json = response.json()
        # 登录成功
        if response_json['retcode'] in [20000000]:
            print('[INFO]: Account -> %s, login successfully' % username)
            infos_return = {'username': username}
            infos_return.update(response_json)
            return infos_return, self.session
        # 用户名或密码错误
        elif response_json['retcode'] in [50011002]:
            raise RuntimeError('Account -> %s, fail to login, username or password error' % username)
        # 安全验证
        elif response_json['retcode'] in [50050011]:
            response = self.session.get(response_json['data']['errurl'])
            msg_type, tip_content, num_times = 'sms', 'You have to secondverify your account, please input the sms code your phone received: ', 0
            response_json = self.__sendverificationcode(username, msg_type=msg_type, verification_page=response)
            while response_json['retcode'] not in [100000]:
                num_times += 1
                if num_times > 1: raise RuntimeError(response_json.get('msg'))
                if response_json['retcode'] in [8513]:
                    msg_type, tip_content = 'private_msg', 'You have to secondverify your account, please input the verification code in your private message: '
                    response_json = self.__sendverificationcode(username, msg_type=msg_type, verification_page=response)
                    break
                else:
                    raise RuntimeError(response_json.get('msg'))
            code = input(tip_content)
            params = {
                'code': code,
                'msg_type': msg_type,
            }
            response = self.session.get(self.ajcheck_url, params=params)
            response_json = response.json()
            if response_json['retcode'] not in [100000]:
                raise RuntimeError(response_json.get('msg'))
            login_url = response_json['data']['url']
            self.session.get(login_url)
            print('[INFO]: Account -> %s, login successfully' % username)
            infos_return = {'username': username}
            infos_return.update(response_json)
            return infos_return, self.session
        # 其他错误
        else:
            raise RuntimeError(response_json['msg'])
    '''安全验证, 发送验证码'''
    def __sendverificationcode(self, username=None, msg_type='sms', verification_page=None):
        assert msg_type in ['sms', 'private_msg']
        params = {'msg_type': msg_type}
        if msg_type == 'sms':
            infos = json.loads(re.search(r'phoneList: JSON.parse\(\'([^\']+)\'\),', verification_page.text).group(1))
            params.update({
                'number': infos[0]['number'],
                'mask_mobile': infos[0]['maskMobile'],
            })
        else:
            self.session.get('https://passport.weibo.cn/signin/secondverify/index', params={'way': 'private_msg'})
        response = self.session.get(self.ajsend_url, params=params)
        return response.json()
    '''初始化'''
    def __initialize(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
        }
        self.login_headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
            'Content': 'application/x-www-form-urlencoded',
            'Origin': 'https://passport.sina.cn',
            'Referer': 'https://passport.sina.cn/signin/signin'
        }
        self.login_url = 'https://passport.sina.cn/sso/login'
        self.ajsend_url = 'https://passport.weibo.cn/signin/secondverify/ajsend'
        self.ajcheck_url = 'https://passport.weibo.cn/signin/secondverify/ajcheck'
        self.session.headers.update(self.headers)


'''移动端登录微博'''
class weiboMobile():
    is_callable = True
    def __init__(self, **kwargs):
        for key, value in kwargs.items(): setattr(self, key, value)
        self.info = 'login in weibo in mobile mode'
        self.session = requests.Session()
        self.__initialize()
    '''登录函数'''
    def login(self, username, password, crack_captcha_func=None, **kwargs):
        # 设置代理
        self.session.proxies.update(kwargs.get('proxies', {}))
        # 模拟登录
        data = {
            'username': username,
            'password': password,
            'savestate': '1',
            'ec': '1',
            'pagerefer': '',
            'entry': 'wapsso',
            'sinacnlogin': '1',
        }
        response = self.session.post(self.login_url, headers=self.login_headers, data=data)
        response_json = response.json()
        # 登录成功
        if response_json['retcode'] in [20000000]:
            print('[INFO]: Account -> %s, login successfully' % username)
            infos_return = {'username': username}
            infos_return.update(response_json)
            return infos_return, self.session
        # 用户名或密码错误
        elif response_json['retcode'] in [50011002]:
            raise RuntimeError('Account -> %s, fail to login, username or password error' % username)
        # 安全验证
        elif response_json['retcode'] in [50050011]:
            response = self.session.get(response_json['data']['errurl'])
            msg_type, tip_content, num_times = 'sms', 'You have to secondverify your account, please input the sms code your phone received: ', 0
            response_json = self.__sendverificationcode(username, msg_type=msg_type, verification_page=response)
            while response_json['retcode'] not in [100000]:
                num_times += 1
                if num_times > 1: raise RuntimeError(response_json.get('msg'))
                if response_json['retcode'] in [8513]:
                    msg_type, tip_content = 'private_msg', 'You have to secondverify your account, please input the verification code in your private message: '
                    response_json = self.__sendverificationcode(username, msg_type=msg_type, verification_page=response)
                    break
                else:
                    raise RuntimeError(response_json.get('msg'))
            code = input(tip_content)
            params = {
                'code': code,
                'msg_type': msg_type,
            }
            response = self.session.get(self.ajcheck_url, params=params)
            response_json = response.json()
            if response_json['retcode'] not in [100000]:
                raise RuntimeError(response_json.get('msg'))
            login_url = response_json['data']['url']
            self.session.get(login_url)
            print('[INFO]: Account -> %s, login successfully' % username)
            infos_return = {'username': username}
            infos_return.update(response_json)
            return infos_return, self.session
        # 其他错误
        else:
            raise RuntimeError(response_json['msg'])
    '''安全验证, 发送验证码'''
    def __sendverificationcode(self, username=None, msg_type='sms', verification_page=None):
        assert msg_type in ['sms', 'private_msg']
        params = {'msg_type': msg_type}
        if msg_type == 'sms':
            infos = json.loads(re.search(r'phoneList: JSON.parse\(\'([^\']+)\'\),', verification_page.text).group(1))
            params.update({
                'number': infos[0]['number'],
                'mask_mobile': infos[0]['maskMobile'],
            })
        else:
            self.session.get('https://passport.weibo.cn/signin/secondverify/index', params={'way': 'private_msg'})
        response = self.session.get(self.ajsend_url, params=params)
        return response.json()
    '''初始化'''
    def __initialize(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
        }
        self.login_headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
            'Content': 'application/x-www-form-urlencoded',
            'Origin': 'https://passport.sina.cn',
            'Referer': 'https://passport.sina.cn/signin/signin'
        }
        self.login_url = 'https://passport.sina.cn/sso/login'
        self.ajsend_url = 'https://passport.weibo.cn/signin/secondverify/ajsend'
        self.ajcheck_url = 'https://passport.weibo.cn/signin/secondverify/ajcheck'
        self.session.headers.update(self.headers)


'''扫码登录微博'''
class weiboScanqr():
    is_callable = True
    def __init__(self, **kwargs):
        for key, value in kwargs.items(): setattr(self, key, value)
        self.info = 'login in weibo in scanqr mode'
        self.cur_path = os.getcwd()
        self.session = requests.Session()
        self.__initialize()
    '''登录函数'''
    def login(self, username='', password='', crack_captcha_func=None, **kwargs):
        # 设置代理
        self.session.proxies.update(kwargs.get('proxies', {}))
        # 获取二维码
        params = {
            'entry': 'weibo',
            'size': '180',
            'callback': str(int(time.time() * 1000)),
        }
        response = self.session.get(self.qrcode_url, params=params)
        response_json = json.loads(response.text.split('(')[-1].split(')')[0])
        qrid = response_json['data']['qrid']
        imageurl = 'https:' + response_json['data']['image']
        response = self.session.get(imageurl)
        saveImage(response.content, os.path.join(self.cur_path, 'qrcode.jpg'))
        showImage(os.path.join(self.cur_path, 'qrcode.jpg'))
        # 检测二维码状态
        while True:
            params = {
                'entry': 'weibo',
                'qrid': qrid,
                'callback': 'STK_%s' % int(time.time() * 10000)
            }
            response = self.session.get(self.check_url, params=params)
            response_json = json.loads(response.text.split('(')[-1].split(')')[0])
            if response_json['retcode'] in [20000000]: break
            time.sleep(0.5)
        removeImage(os.path.join(self.cur_path, 'qrcode.jpg'))
        # 模拟登录
        params = {
            'entry': 'weibo',
            'returntype': 'TEXT',
            'crossdomain': '1',
            'cdult': '3',
            'domain': 'weibo.com',
            'alt': response_json['data']['alt'],
            'savestate': '30',
            'callback': 'STK_' + str(int(time.time() * 1000)),
        }
        response = self.session.get(self.login_url, params=params)
        response_json = json.loads(response.text.split('(')[-1].split(')')[0])
        response_json['crossDomainUrlList'][0] = response_json['crossDomainUrlList'][0] + '&action=login'
        for url in response_json['crossDomainUrlList']:
            response = self.session.get(url)
        # 登录成功
        response = self.session.get(self.home_url)
        print('[INFO]: Account -> %s, login successfully' % response_json.get('nick', username))
        infos_return = {'username': username}
        infos_return.update(response_json)
        return infos_return, self.session
    '''初始化'''
    def __initialize(self):
        self.headers = {
            'Referer': 'https://mail.sina.com.cn/',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
        }
        self.home_url = 'https://weibo.com'
        self.qrcode_url = 'https://login.sina.com.cn/sso/qrcode/image'
        self.check_url = 'https://login.sina.com.cn/sso/qrcode/check'
        self.login_url = 'http://login.sina.com.cn/sso/login.php'
        self.session.headers.update(self.headers)


'''
Function:
    微博模拟登录
Detail:
    -login:
        Input:
            --username: 用户名
            --password: 密码
            --mode: mobile/pc/scanqr
            --crack_captcha_func: 若提供验证码接口, 则利用该接口来实现验证码的自动识别
            --proxies: 为requests.Session()设置代理
        Return:
            --infos_return: 用户名等信息
            --session: 登录后的requests.Session()
'''
class weibo():
    def __init__(self, **kwargs):
        self.info = 'login in weibo'
        self.supported_modes = {
            'pc': weiboPC(**kwargs),
            'mobile': weiboMobile(**kwargs),
            'scanqr': weiboScanqr(**kwargs),
        }
    '''登录函数'''
    def login(self, username='', password='', mode='scanqr', crack_captcha_func=None, **kwargs):
        assert mode in self.supported_modes, 'unsupport mode %s in weibo.login' % mode
        selected_api = self.supported_modes[mode]
        if not selected_api.is_callable: raise NotImplementedError('not be implemented for mode %s in weibo.login' % mode)
        args = {
            'username': username,
            'password': password,
            'crack_captcha_func': crack_captcha_func,
        }
        args.update(kwargs)
        return selected_api.login(**args)