# -*- coding: utf-8 -*-

import re
import random
import json
import os
import sys
import datetime
import time
import threading
import logging
import urllib
import smtplib
from HttpClient import HttpClient
from email.Header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

reload(sys)
sys.setdefaultencoding("utf-8")

#SET YOUR OWN PARAMETERS HERE
tulingkey = 'c7c5abbc9ec9cad3a63bde71d17e3c2c'
mailserver = 'smtp.126.com'
mailsig = 'QQParking Notification'
mailuser = 'qqparking@126.com'
mailpass = 'uyyxdrzrrxntidkh'
#-----END OF SECTION-------

#LOADED FROM FILE
sendtomail = '#send to which mail box. e.g.: recv@gmail.com'
welcomeMessage = '您好,我现在不在电脑旁!'
#---END OF SECTION---

HttpClient_Ist = HttpClient()

ClientID = 53999199
PTWebQQ = ''
APPID = 0
msgId = 0
ThreadList = []
MailThreadList = []
PSessionID = ''

Referer = 'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1'
httpsReferer = 'https://d1.web2.qq.com/cfproxy.html?v=20151105001&callback=1'
SmartQQUrl = 'https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20131024001'


VFWebQQ = ''
AdminQQ = '0'

#My QQ
MyUIN = 0
QQUserName = ''
#Put UserNameList Here to avoid multiple requests
MarkNameList = []
NickNameList = []
GroupList = []
DiscussionList = []

initTime = time.time()


logging.basicConfig(filename='log.log', level=logging.DEBUG, format='%(asctime)s  %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')

# -----------------
# 方法声明
# -----------------

#Encryption Algorithm Used By QQ
def gethash(selfuin, ptwebqq):
    selfuin += ""
    N=[0,0,0,0]
    for T in range(len(ptwebqq)):
        N[T%4]=N[T%4]^ord(ptwebqq[T])
    U=["EC","OK"]
    V=[0, 0, 0, 0]
    V[0]=int(selfuin) >> 24 & 255 ^ ord(U[0][0])
    V[1]=int(selfuin) >> 16 & 255 ^ ord(U[0][1])
    V[2]=int(selfuin) >>  8 & 255 ^ ord(U[1][0])
    V[3]=int(selfuin)       & 255 ^ ord(U[1][1])
    U=[0,0,0,0,0,0,0,0]
    U[0]=N[0]
    U[1]=V[0]
    U[2]=N[1]
    U[3]=V[1]
    U[4]=N[2]
    U[5]=V[2]
    U[6]=N[3]
    U[7]=V[3]  
    N=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
    V=""
    for T in range(len(U)):
        V+= N[ U[T]>>4 & 15]
        V+= N[ U[T]    & 15]
    return V

def get_ts():
    ts = time.time()
    while ts < 1000000000000:
        ts = ts * 10
    ts = int(ts)
    return ts

def CProcess(content):
    return str(content.replace("\\", r"\\").replace("\n", r"\n").replace("\r", r"\r").replace("\t", r"\t").replace('"', r'\"'))

def getQRtoken(qrsig):
    e = 0
    for i in qrsig:
        e += (e << 5) + ord(i)
    return 2147483647 & e;

def pass_time():
    global initTime
    rs = (time.time() - initTime)
    initTime = time.time()
    return str(round(rs, 3))


def getReValue(html, rex, er, ex):
    v = re.search(rex, html)

    if v is None:
        logging.error(er)

        if ex:
            raise Exception, er
        return ''

    return v.group(1)

def sendfailmail():
    global QQUserName, MyUIN
    try:
        SUBJECT = 'QQ挂机下线提醒: '+str(QQUserName)+'[QQ号:'+str(MyUIN)+']'
        TO = [sendtomail]
        msg = MIMEMultipart('alternative')
        msg['Subject'] = Header(SUBJECT, 'utf-8')
        msg['From'] = mailsig+'<'+mailuser+'>'
        msg['To'] = ', '.join(TO)
        part = MIMEText("Fatal error occured. Please restart the program and login again!", 'plain', 'utf-8')
        msg.attach(part)
        server = smtplib.SMTP(mailserver, 25)
        server.login(mailuser, mailpass)
        server.login(mailuser, mailpass)
        server.sendmail(mailuser, TO, msg.as_string())
        server.quit()
        return True
    except Exception , e:
        logging.error("发送程序错误邮件失败:"+str(e))
        return False
def date_to_millis(d):
    return int(time.mktime(d.timetuple())) * 1000

def msg_handler(msgObj):
    for msg in msgObj:
        msgType = msg['poll_type']

        # QQ私聊消息
        if msgType == 'message' or msgType == 'sess_message':  # 私聊 or 临时对话
            txt = combine_msg(msg['value']['content'])
            tuin = msg['value']['from_uin']
            msg_id = msg['value']['msg_id']

            # print "{0}:{1}".format(from_account, txt)
            thread_cleanup()    
            targetThread = thread_exist(tuin)
            if targetThread:
                targetThread.push(txt, msg_id)
            else:
                try:
                    service_type = 0
                    isSess = 0
                    group_sig = ''
                    myid = ''
                    if msgType == 'sess_message':
                        isSess = 1
                        service_type = msg['value']['service_type']
                        myid = msg['value']['id'] 
                        info = json.loads(HttpClient_Ist.Get('http://d1.web2.qq.com/channel/get_c2cmsg_sig2?id={0}&to_uin={1}&clientid={2}&psessionid={3}&service_type={4}&t={5}'.format(myid, tuin, ClientID, PSessionID, service_type, get_ts()), Referer))
                        logging.info("Get group sig:" + str(info))
                        if info['retcode'] != 0:
                            raise ValueError, info
                        info = info['result']
                        group_sig = info['value']
                    tmpThread = pmchat_thread(tuin,isSess,group_sig,service_type,txt,msg_id,myid)
                    tmpThread.start()
                    ThreadList.append(tmpThread)
                    logging.info("add thread "+str(tmpThread)+" for qq"+str(tuin))
                except Exception, e:
                    logging.info("error"+str(e))

            # print "{0}:{1}".format(self.FriendList.get(tuin, 0), txt)

            # if FriendList.get(tuin, 0) == AdminQQ:#如果消息的发送者与AdminQQ不相同, 则忽略本条消息不往下继续执行
            #     if txt[0] == '#':
            #         thread.start_new_thread(self.runCommand, (tuin, txt[1:].strip(), msgId))
            #         msgId += 1

            # if txt[0:4] == 'exit':
            #     logging.info(self.Get('http://d1.web2.qq.com/channel/logout2?ids=&clientid={0}&psessionid={1}'.format(self.ClientID, self.PSessionID), Referer))
            #     exit(0)

        # QQ号在另一个地方登陆, 被挤下线
        if msgType == 'kick_message':
            logging.error(msg['value']['reason'])
            raise Exception, msg['value']['reason']  # 抛出异常, 重新启动WebQQ, 需重新扫描QRCode来完成登陆


def combine_msg(content):
    msgTXT = ""
    for part in content:
        # print type(part)
        if type(part) == type(u'\u0000'):
            msgTXT += part
        elif len(part) > 1:
            # 如果是图片
            if str(part[0]) == "offpic" or str(part[0]) == "cface":
                msgTXT += "[图片]"

    return msgTXT


def send_msg(tuin, content, isSess, group_sig, service_type):
    if isSess == 0:
        reqURL = "https://d1.web2.qq.com/channel/send_buddy_msg2"
        data = (
            ('r', '{{"to":{0}, "face":594, "content":"[\\"{4}\\", [\\"font\\", {{\\"name\\":\\"Arial\\", \\"size\\":\\"10\\", \\"style\\":[0, 0, 0], \\"color\\":\\"000000\\"}}]]", "clientid":{1}, "msg_id":{2}, "psessionid":"{3}"}}'.format(tuin, ClientID, msgId, PSessionID, CProcess(content))),
            ('clientid', ClientID),
            ('psessionid', PSessionID)
        )
        rsp = HttpClient_Ist.Post(reqURL, data, httpsReferer)
        try:
            rspp = json.loads(rsp)
            if rspp['errCode']!= 0:
                logging.error("reply pmchat error"+str(rspp['errCode']))
                return False
            return True
        except:
            pass
    else:
        reqURL = "https://d1.web2.qq.com/channel/send_sess_msg2"
        data = (
            ('r', '{{"to":{0}, "face":594, "content":"[\\"{4}\\", [\\"font\\", {{\\"name\\":\\"Arial\\", \\"size\\":\\"10\\", \\"style\\":[0, 0, 0], \\"color\\":\\"000000\\"}}]]", "clientid":{1}, "msg_id":{2}, "psessionid":"{3}", "group_sig":"{5}", "service_type":{6}}}'.format(tuin, ClientID, msgId, PSessionID, CProcess(content), group_sig, service_type)),
            ('clientid', ClientID),
            ('psessionid', PSessionID),
            ('group_sig', group_sig),
            ('service_type',service_type)
        )
        rsp = HttpClient_Ist.Post(reqURL, data, httpsReferer)
        try:
            rspp = json.loads(rsp)
            if rspp['errCode']!= 0:
                logging.error("reply temp pmchat error"+str(rspp['errCode']))
                return False
            return True
        except:
            pass
    return False


def thread_exist(tuin):
    for t in ThreadList:
        if t.isAlive():
            if t.tuin == tuin:
                t.check()
                return t
    return False

def thread_cleanup():
    for t in ThreadList:
        if not t.isAlive():
            ThreadList.remove(t)
    for t in MailThreadList:
        if not t.isAlive():
            MailThreadList.remove(t)
    return True

class send_mail(threading.Thread):
    
    def __init__(self, uin, content):
        threading.Thread.__init__(self)
        self.content = content
        self.uin = uin
    def run(self):
        global MarkNameList,NickNameList
        try:
            flag=0
            for t in NickNameList:
                if str(t["uin"])==str(self.uin):
                    hisnick=t["nick"]
                    flag=1
                    break
            if flag==0:
                raise ValueError, "Unable to find nick name"
            for t in MarkNameList:
                if str(t["uin"])==str(self.uin):
                    hismark=t["markname"]
                    flag=2
                    break
            if flag==1:
                subinfo="昵称:"+str(hisnick)
            else:
                subinfo=str(hismark)+"(昵称:"+str(hisnick)+")"
            flag=0
            while not self.smtpmail(subinfo):
                flag = flag + 1
                if flag > 3:
                    raise ValueError, flag
                    break
            return True
        except Exception , e:
            self.failmsg()
            logging.error("error sending msg for :"+str(e)+" times (send fail reply now)")
            return False
    def smtpmail(self,subinfo):
        try:
            SUBJECT = '来自 '+subinfo+'的留言'
            TO = [sendtomail]
            msg = MIMEMultipart('alternative')
            msg['Subject'] = Header(SUBJECT, 'utf-8')
            msg['From'] = mailsig+'<'+mailuser+'>'
            msg['To'] = ', '.join(TO)
            part = MIMEText(self.content, 'plain', 'utf-8')
            msg.attach(part)        
            server = smtplib.SMTP(mailserver, 25)
            server.login(mailuser, mailpass)
            server.login(mailuser, mailpass)
            server.sendmail(mailuser, TO, msg.as_string())
            server.quit()
            return True
        except Exception, e:
            logging.error("error sending msg:"+str(e))
            return False
    def failmsg(self):
        targetThread = thread_exist(int(self.uin))
        logging.info("邮件发送失败提示,push进线程:"+str(targetThread))
        if targetThread:
            targetThread.reply("抱歉,留言发送失败,留言内容为:\n"+str(self.content))
        return True
        
class send_sess_mail(threading.Thread):
    
    def __init__(self, uin, content, sess_group_id, service_type):
        threading.Thread.__init__(self)
        self.content = content
        self.uin = uin
        self.sess_group_id = sess_group_id
        self.service_type = service_type
    def run(self):
        try:
            subinfo,gcode,gname = self.get_display_name()
            SUBJECT = '来自(临时对话) '+subinfo+'的留言'
            if self.service_type == 0:
                SUBJECT = SUBJECT + "(来自群:"+gname+")"
            else:
                SUBJECT = SUBJECT + "(来自讨论组:"+gname+")"
            flag=1
            while not self.smtpmail(SUBJECT):
                flag = flag + 1
                if flag > 3:
                    raise ValueError, flag
                    break
            return True
        except Exception , e:
            self.failmsg()
            logging.error("error sending msg for :"+str(e)+" times (send fail reply now)")
            return False
    def smtpmail(self,SUBJECT):
        try:
            TO = [sendtomail]
            msg = MIMEMultipart('alternative')
            msg['Subject'] = Header(SUBJECT, 'utf-8')
            msg['From'] = mailsig+'<'+mailuser+'>'
            msg['To'] = ', '.join(TO)
            part = MIMEText(self.content, 'plain', 'utf-8')
            msg.attach(part)        
            server = smtplib.SMTP(mailserver, 25)
            server.login(mailuser, mailpass)
            server.login(mailuser, mailpass)
            server.sendmail(mailuser, TO, msg.as_string())
            server.quit()
            return True
        except Exception, e:
            logging.error("error sending msg:"+str(e))
            return False
    def get_display_name(self):
        global GroupList, DiscussionList
        #群临时对话
        flag=0
        if self.service_type == 0:
            for t in GroupList:
                if str(t["gid"])==str(self.sess_group_id):
                    group_name=t["name"]
                    group_code=t["code"]
                    flag=1
                    break
            if flag==0:
                raise ValueError, "Unable to find corresponding group"
            
            html = HttpClient_Ist.Get('http://s.web2.qq.com/api/get_group_info_ext2?gcode={0}&vfwebqq={1}&t={2}'.format(group_code, VFWebQQ,get_ts()), Referer)
            ret = json.loads(html)
            if ret['retcode']!= 0:
                raise ValueError, "retcode error when getting group detail info: retcode="+ret['retcode']
            flag=0
            for t in ret['result']['minfo']:
                if str(t["uin"])==str(self.uin):
                    hisnick=t["nick"]
                    flag=1
                    break
            if flag==0:
                raise ValueError, "Unable to find nick name in sess from_group"
            for t in ret['result']['cards']:
                if str(t["muin"])==str(self.uin):
                    hismark=t["card"]
                    flag=2
                    break
            if flag==1:
                subinfo="昵称:"+str(hisnick)
            else:
                subinfo=str(hismark)+"(昵称:"+str(hisnick)+")"
            return (subinfo,group_code,group_name)
        else:
            for t in DiscussionList:
                if str(t["did"])==str(self.sess_group_id):
                    group_name=t["name"]
                    flag=1
                    break
            if flag==0:
                raise ValueError, "Unable to find corresponding discussion group"
            
            html = HttpClient_Ist.Get('http://d1.web2.qq.com/channel/get_discu_info?did={0}&vfwebqq={1}&clientid={2}&psessionid={3}&t={4}'.format(self.sess_group_id, VFWebQQ, ClientID,PSessionID,get_ts()), Referer)
            ret = json.loads(html)
            if ret['retcode']!= 0:
                raise ValueError, "retcode error when getting discussion group detail info: retcode="+ret['retcode']
            flag=0
            for t in ret['result']['mem_info']:
                if str(t["uin"])==str(self.uin):
                    hisnick=t["nick"]
                    flag=1
                    break
            if flag==0:
                raise ValueError, "Unable to find nick name in sess from_discussion_group"
            subinfo="昵称:"+str(hisnick)
            return (subinfo,self.sess_group_id,group_name)
            
    def failmsg(self):
        targetThread = thread_exist(int(self.uin))
        logging.info("邮件发送失败提示,push进线程:"+str(targetThread))
        if targetThread:
            targetThread.reply("抱歉,留言发送失败,留言内容为:\n"+str(self.content))
        return True
# -----------------
# 类声明
# -----------------


class Login(HttpClient):
    MaxTryTime = 5

    def __init__(self, vpath, qq=0):
        global APPID, AdminQQ, PTWebQQ, VFWebQQ, PSessionID, msgId, MyUIN,MarkNameList,NickNameList,GroupList,DiscussionList,QQUserName, welcomeMessage, sendtomail
        self.VPath = vpath  # QRCode保存路径
        AdminQQ = int(qq)

        f=open('config.txt','rt')
        sendtomail=f.readline().replace("\n","").replace("\r","")
        msg=f.readline().replace("\n","").replace("\r","")
        f.close()
        if sendtomail=='':
            raise ValueError, 'MUST INPUT NOTIFICATION MAILBOX! (错误:QQParking必须输入邮箱!程序已退出)'
        if msg!='':
            welcomeMessage = msg
        logging.info("配置: 提醒邮箱:"+str(sendtomail)+";欢迎信息:"+str(welcomeMessage))

        logging.critical("正在获取登陆页面")
        self.Get('http://w.qq.com/')
        html = self.Get(SmartQQUrl,'http://w.qq.com/')
        logging.critical("正在获取appid")
        APPID = getReValue(html, r'<input type="hidden" name="aid" value="(\d+)" />', 'Get AppId Error', 1)
        logging.critical("正在获取login_sig")
        sign = getReValue(html, r'g_login_sig\s*=\s*encodeURIComponent\s*\("(.*?)"\)', 'Get Login Sign Error', 0)
        logging.info('get sign : %s', sign)
        logging.critical("正在获取pt_version")
        JsVer = getReValue(html, r'g_pt_version\s*=\s*encodeURIComponent\s*\("(\d+)"\)', 'Get g_pt_version Error', 1)
        logging.info('get g_pt_version : %s', JsVer)
        logging.critical("正在获取mibao_css")
        MiBaoCss = getReValue(html, r'g_mibao_css\s*=\s*encodeURIComponent\s*\("(.*?)"\)', 'Get g_mibao_css Error', 1)
        logging.info('get g_mibao_css : %s', sign)
        StarTime = date_to_millis(datetime.datetime.utcnow())
        T = 0
        while True:
            T = T + 1
            self.Download('https://ssl.ptlogin2.qq.com/ptqrshow?appid={0}&e=0&l=M&s=5&d=72&v=4&t=0.0836106{1}4250{2}6653'.format(APPID,random.randint(0,9),random.randint(0,9)), self.VPath)

            logging.info('[{0}] Get QRCode Picture Success.'.format(T))

            QRSig = self.getCookie('qrsig')
            while True:
                html = self.Get('https://ssl.ptlogin2.qq.com/ptqrlogin?ptqrtoken={0}&webqq_type=10&remember_uin=1&login2qq=1&aid={1}&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=0-0-{2}&mibao_css={3}&t=1&g=1&js_type=0&js_ver={4}&login_sig={5}&pt_randsalt=2'.format(getQRtoken(QRSig),APPID, date_to_millis(datetime.datetime.utcnow()) - StarTime, MiBaoCss, JsVer, sign),
                        SmartQQUrl)
                # logging.info(html)
                ret = html.split("'")
                if ret[1] == '65' or ret[1] == '0':  # 65: QRCode 失效, 0: 验证成功, 66: 未失效, 67: 验证中
                    break
                time.sleep(2)
            if ret[1] == '0' or T > self.MaxTryTime:
                break

        logging.info(ret)
        if ret[1] != '0':
            raise ValueError, "RetCode = "+ret['retcode']
            return
        logging.critical("二维码已扫描,正在登陆")
        pass_time()
        # 删除QRCode文件
        if os.path.exists(self.VPath):
            os.remove(self.VPath)

        # 记录登陆账号的昵称
        tmpUserName = ret[11]

        html = self.Get(ret[5])
        url = getReValue(html, r' src="(.+?)"', 'Get mibao_res Url Error.', 0)
        if url != '':
            html = self.Get(url.replace('&amp;', '&'))
            url = getReValue(html, r'location\.href="(.+?)"', 'Get Redirect Url Error', 1)
            html = self.Get(url)

        PTWebQQ = self.getCookie('ptwebqq')

        logging.info('PTWebQQ: {0}'.format(PTWebQQ))

        LoginError = 3
        while LoginError > 0:
            try:
                html = self.Post('http://d1.web2.qq.com/channel/login2', {
                    'r': '{{"ptwebqq":"{0}","clientid":{1},"psessionid":"{2}","status":"online"}}'.format(PTWebQQ, ClientID, PSessionID)
                }, 'http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2')
                ret = json.loads(html)
                html2 = self.Get("http://s.web2.qq.com/api/getvfwebqq?ptwebqq={0}&clientid={1}&psessionid={2}&t={3}".format(PTWebQQ, ClientID, PSessionID, get_ts()), Referer)
                logging.info("getvfwebqq html:  " + str(html2))
                ret2 = json.loads(html2)
                LoginError = 0
            except:
                LoginError -= 1
                logging.critical("登录失败,正在重试")

        if ret['retcode'] != 0 or ret2['retcode'] != 0:
            raise ValueError, "Login Retcode="+str(ret['retcode'])
            return

        VFWebQQ = ret2['result']['vfwebqq']
        PSessionID = ret['result']['psessionid']
        MyUIN = ret['result']['uin']
        logging.critical("QQ号:{0} 登陆成功, 用户名:{1}".format(ret['result']['uin'], tmpUserName))
        logging.info('Login success')
        logging.critical("登陆二维码用时" + pass_time() + "秒")
        QQUserName = tmpUserName
        msgId = int(random.uniform(20000, 50000))
        
        self.Get('http://d1.web2.qq.com/channel/get_online_buddies2?vfwebqq={0}&clientid={1}&psessionid={2}&t={3}'.format(VFWebQQ,ClientID,PSessionID,get_ts()),Referer)

        html = self.Post('http://s.web2.qq.com/api/get_user_friends2', {
                'r': '{{"vfwebqq":"{0}","hash":"{1}"}}'.format(str(VFWebQQ),gethash(str(MyUIN),str(PTWebQQ)))
            }, Referer)
        ret = json.loads(html)
        if ret['retcode']!= 0:
            raise ValueError, "retcode error when getting friends list: retcode="+ret['retcode']
        NickNameList = ret['result']['info']
        MarkNameList = ret['result']['marknames']
        html = self.Get('http://s.web2.qq.com/api/get_discus_list?clientid={0}&psessionid={1}&vfwebqq={2}&t={3}'.format(ClientID, PSessionID, VFWebQQ,get_ts()), Referer)
        ret = json.loads(html)
        if ret['retcode']!= 0:
            raise ValueError, "retcode error when getting discussion group list: retcode="+ret['retcode']
        DiscussionList = ret['result']['dnamelist']
        html = self.Post('http://s.web2.qq.com/api/get_group_name_list_mask2', {
                'r': '{{"vfwebqq":"{0}","hash":"{1}"}}'.format(str(VFWebQQ),gethash(str(MyUIN),str(PTWebQQ)))
            }, Referer)
        ret = json.loads(html)
        if ret['retcode']!= 0:
            raise ValueError, "retcode error when getting group list: retcode="+str(ret['retcode'])
        GroupList = ret['result']['gnamelist']
        
class check_msg(threading.Thread):
    # try:
    #   pass
    # except KeybordInterrupt:
    #   try:
    #     user_input = (raw_input("回复系统:(输入格式:{群聊2or私聊1}, {群号or账号}, {内容})\n")).split(",")
    #     if (user_input[0] == 1):

    #       for kv in self.FriendList :
    #         if str(kv[1]) == str(user_input[1]):
    #           tuin == kv[0]

    #       self.send_msg(tuin, user_input[2])

    #   except KeybordInterrupt:
    #     exit(0)
    #   except Exception, e:
    #     print Exception, e

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global PTWebQQ
        E = 0
        # 心跳包轮询
        while 1:
            if E > 5:
                break
            try:
                ret = self.check()
            except:
                E += 1
                continue
            # logging.info(ret)

            # 返回数据有误
            if ret == "":
                E += 1
                continue

            # POST数据有误
            if ret['retcode'] == 100006:
                break

            # 无消息
            if ret['retcode'] == 102:
                E = 0
                continue

            # 更新PTWebQQ值
            if ret['retcode'] == 116:
                PTWebQQ = ret['p']
                E = 0
                continue

            if ret['retcode'] == 0:
                # 信息分发
                if 'result' in ret:
                    msg_handler(ret['result'])
                E = 0
                continue
            
            # Exit on abnormal retcode
            E += 1
            HttpClient_Ist.Get('http://d1.web2.qq.com/channel/get_online_buddies2?vfwebqq={0}&clientid={1}&psessionid={2}&t={3}'.format(VFWebQQ,ClientID,PSessionID,get_ts()),Referer)

        logging.critical("轮询错误超过五次")

    # 向服务器查询新消息
    def check(self):

        html = HttpClient_Ist.Post('https://d1.web2.qq.com/channel/poll2', {
            'r': '{{"ptwebqq":"{1}","clientid":{2},"psessionid":"{0}","key":""}}'.format(PSessionID, PTWebQQ, ClientID)
        }, httpsReferer)
        logging.info("Check html: " + str(html))
        try:
            ret = json.loads(html)
        except Exception as e:
            logging.error(str(e))
            logging.critical("Check error occured, retrying.")
            return self.check()

        return ret


class pmchat_thread(threading.Thread):

    def __init__(self, tuin, isSess, group_sig, service_type,ini_txt,ini_msgid,myid):
        threading.Thread.__init__(self)
        self.tuin = tuin
        self.isSess = isSess
        self.group_sig=group_sig
        self.service_type=service_type
        self.lastcheck = time.time()
        self.lastseq=0
        self.lastmail=0
        self.isrecord=0
        self.ini_txt=ini_txt
        self.ini_msgid=ini_msgid
        self.sess_group_id = myid
        self.replystreak = 0
        self.autoreply=welcomeMessage+' 接下来由小黄鸡代我与您聊天!在聊天时输入【record】可以开始给我留言,(英文单词: record),输入此命令并在收到提示后输入留言内容即可.record前面不能有空格(r需为该消息的第一个字符)'
    def check(self):
        self.lastcheck = time.time()
    def run(self):
        logging.info("私聊线程生成,私聊对象:"+str(self.tuin))
        self.awaymsgsucc = self.reply(self.autoreply)
        self.push(self.ini_txt,self.ini_msgid)
        while self.awaymsgsucc:
            time.sleep(119)
            if time.time() - self.lastcheck > 800:
                break

    def reply(self, content):
        failtimes = 0
        while not send_msg(self.tuin, str(content)+"(此消息来自小黄鸡,非本人)", self.isSess, self.group_sig, self.service_type):
            failtimes = failtimes + 1
            if failtimes >= 3:
                break
            time.sleep(1)
        if failtimes < 3:
            logging.info("Reply to UIN " + str(self.tuin) + ":" + str(content))
            return True
        else:
            logging.error("FAIL TO Reply to UIN " + str(self.tuin) + ":" + str(content))
            return False
    def record_important(self, content):
        pattern = re.compile(r'^(record)') 
        match = pattern.match(content)
        try:
            if match:
                if time.time() - self.lastmail < 300.0:
                    logging.info("EMAIL TOO FAST, ABANDON:"+content)
                    self.reply("您留言太频繁了,请5分钟后重试!")
                    return True
                self.lastmail = time.time()
                logging.info("start recording important message")
                self.reply("请回复您需要留言的内容,请将所有内容合并在一条回复中(可分行)。您的昵称与备注名将自动被记录,请留下联系方式以便我回复您!")
                self.isrecord = 1
                return True
            return False
        except Exception, e:
            logging.error("ERROR"+str(e))
        return False
    def record(self, content):
        try:
            if self.isrecord==0:
                return False
            if self.isrecord==1:
                self.isrecord = 0
                if self.isSess == 0:
                    tmpthread = send_mail(str(self.tuin),str(content).decode('UTF-8'))
                else:
                    tmpthread = send_sess_mail(str(self.tuin),str(content).decode('UTF-8'),str(self.sess_group_id),self.service_type)
                tmpthread.start()
                MailThreadList.append(tmpthread)
                self.reply("此消息已记录,主人会尽快回复!记录的内容如下\n"+str(content))
                return True
            return False
        except Exception, e:
            logging.error("ERROR"+str(e))
        return False
    def push(self, ipContent, seq):
        if seq == self.lastseq:
            return True
        else:
            self.lastseq=seq

        try:
            if self.record(ipContent):
                return True
            if self.record_important(ipContent):
                return True
            self.replystreak = self.replystreak + 1
            #防止机器人对聊
            if self.replystreak>30:
                self.replystreak = 0
                return True
            logging.info("PM get info from AI: "+ipContent)
            paraf={ 'userid' : str(self.tuin), 'key' : tulingkey, 'info' : ipContent}
            info = HttpClient_Ist.Get('http://www.tuling123.com/openapi/api?'+urllib.urlencode(paraf))
            logging.info("AI REPLY:"+str(info))
            info = json.loads(info)
            if info["code"] in [40001, 40003, 40004]:
                self.reply("我今天累了,不聊了")
                logging.warning("Reach max AI call")
            elif info["code"] in [40002, 40005, 40006, 40007]:
                self.reply("我遇到了一点问题,请稍后@我")
                logging.warning("PM AI return error, code:"+str(info["code"]))
            else:
                rpy = str(info["text"]).replace('<主人>','你').replace('<br>',"\n")
                self.reply(rpy)
            return True
        except Exception, e:
            logging.error("ERROR:"+str(e))
        return False

# -----------------
# 主程序
# -----------------

if __name__ == "__main__":
    vpath = './v.png'
    qq = 0
    if len(sys.argv) > 1:
        vpath = sys.argv[1]
    if len(sys.argv) > 2:
        qq = sys.argv[2]

    try:
        pass_time()
        qqLogin = Login(vpath, qq)
    except Exception, e:
        logging.error(str(e))
        os._exit(1)
    try:
        t_check = check_msg()
        t_check.setDaemon(True)
        t_check.start()
        t_check.join()
    except:
        pass
    errortime=0
    logging.info("发送下线邮件...")
    while errortime<5:
        errortime=errortime+1
        if sendfailmail():
            break