Public Class WebQQ_Client
    Public Property QQ_Number As String
    Public Property QQ_Password As String
    Public Property Clientid As String
    Public Property UserName As String 'Do not modify!
    Public Property Listening_Thread As New System.Threading.Thread(AddressOf Listening_Thread_Proc) 'Do not modify!
    Public Property CMD_Head As String = "cmd"
    Public Property Administrator As String        'Use uid to set the administrator
    Public Property CMD_Password As String

    Public Verify_Code, Encrypt_Code As String     'Do not modify!
    Public Cookies As New Net.CookieCollection     'Do not modify!
    Public ptwebqq, psessionid, vfwebqq As String  'Do not modify!

#Region "Events"
    Public Event On_QQ_Message_Arrive(ByVal Message As QQ_Message)
    Public Event On_Polls_Arrive(ByVal Poll As String, ByRef Cancel As Boolean)
    Public Event On_Command_Arrive(ByVal sender As String, ByVal group As String, ByVal cmd As String, ByVal Param As String)
    Public Event On_Error(ByVal Err As String)
    Public Event Ext_Poll_Proc(ByVal Message As Newtonsoft.Json.Linq.JToken) 'system_message, buddies_status_change, input_notify
    Public Event On_Finish_Sending(ByVal Result As String, target As String, Messages As Newtonsoft.Json.Linq.JToken(), IsGroup As Boolean, MessageId As Integer)
    Public Event On_Quit_Request()
    Public Event On_Log(Text As String)
#End Region

#Region "Properties"
    Dim _Friend_List As QQ_Friend_List
    Public ReadOnly Property Friend_List As QQ_Friend_List
        Get
            Return _Friend_List
        End Get
    End Property

    Dim _Group_List As QQ_Group_List
    Public ReadOnly Property Groups As QQ_Group_List
        Get
            Return _Group_List
        End Get
    End Property
#End Region

    Public Sub New(Optional ByVal _QQ_Number As String = "", Optional ByVal _QQ_Password As String = "", Optional _ClientId As String = "", Optional _CMD_Password As String = "123456")
        QQ_Number = _QQ_Number
        QQ_Password = _QQ_Password
        Clientid = _ClientId
        CMD_Password = _CMD_Password

        If Clientid = "" Then Clientid = 800000 + Rnd() * 100000
    End Sub

    Public Function Check_Verify_Code() As Boolean
        With Http.HttpWebResponseUtility.CreateGetHttpResponse("http://ptlogin2.qq.com/check?" + "uin=" + QQ_Number + "&appid=1003903&r=0.23301555978693034", -1, "", Cookies)
            Verify_Code = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd
            Cookies.Add(.Cookies)
        End With
        'WriteLine("Feedback: " + Verify_Code)                     'ptui_checkVC('0','!OSC','\x00\x00\x00\x00\x5c\xa4\x73\x8a');
        Verify_Code = Verify_Code.Replace("'", Nothing)          'ptui_checkVC(0,!OSC,\x00\x00\x00\x00\x5c\xa4\x73\x8a);
        Verify_Code = Verify_Code.Replace("ptui_checkVC(", Nothing).Replace(");", Nothing) '0,!OSC,\x00\x00\x00\x00\x5c\xa4\x73\x8a
        If Verify_Code.StartsWith("0") Then                      'Determine whether the user need to input the verify code or not
            Verify_Code = Split(Verify_Code, ",")(1)             '!OSC
        Else
            With New Form_Verifying
                Dim TempPicFile As String = System.IO.Path.GetTempFileName()
                My.Computer.Network.DownloadFile("http://captcha.qq.com/getimage?aid=1003903&&uin=" + QQ_Number + "&vc_type=" + Split(Verify_Code, ",")(1), TempPicFile)
                .PictureBox_Valid.Image = Drawing.Image.FromFile(TempPicFile)
                If .ShowDialog() <> Windows.Forms.DialogResult.OK Then Return False 'Refuse to input the verify code will cause error
                Verify_Code = .TextBox_Code.Text
                .Dispose()
            End With
        End If
        Return True
    End Function

    Public Function Connect() As Boolean
        Encrypt_Code = QQ.QQMd5.Encrypt(QQ_Number, QQ_Password, Verify_Code)    'Calculate the weird encrypt code
        With Http.HttpWebResponseUtility.CreateGetHttpResponse("http://ptlogin2.qq.com/login?u=" + QQ_Number + "&p=" + Encrypt_Code + "&verifycode=" + Verify_Code + "&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=2-17-7145&mibao_css=m_webqq&t=1&g=1", -1, "", Cookies)
            Cookies.Add(.Cookies)
            UserName = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
            If UserName.StartsWith("ptuiCB('0") Then
                UserName = UserName.Split(",")(5).Replace(");", "").Replace("'", "").Trim()
                ptwebqq = Cookies("ptwebqq").Value
                Return True
            Else
                Return False
            End If
        End With
        Return False
    End Function

    Public Sub Logon()
        Dim Login_Info As IDictionary(Of String, String) = New Dictionary(Of String, String)()
        Dim Json_Login_str As String
        Login_Info.Add("r", "{" + Chr(34) + "status" + Chr(34) + ":" + Chr(34) + Chr(34) + "," + Chr(34) + "ptwebqq" + Chr(34) + ":" + Chr(34) + ptwebqq + Chr(34) + "," + Chr(34) + "passwd_sig" + Chr(34) + ":" + Chr(34) + Chr(34) + "," + Chr(34) + "clientid" + Chr(34) + ":" + Chr(34) + Clientid + Chr(34) + "}")
        With Http.HttpWebResponseUtility.CreatePostHttpResponse("http://d.web2.qq.com/channel/login2", Login_Info, -1, "", System.Text.Encoding.UTF8, Cookies, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3")
            Cookies.Add(.Cookies)
            Json_Login_str = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
        End With
        Dim Json_Login As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(Json_Login_str)
        psessionid = Json_Login("result")("psessionid")
        vfwebqq = Json_Login("result")("vfwebqq")
    End Sub

    Public Function Grab_Friend_List() As Boolean
        Try
            Dim Get_Friend_Info As IDictionary(Of String, String) = New Dictionary(Of String, String)()
            Dim Json_Friends_Str As String
            Dim Json_Send As New Newtonsoft.Json.Linq.JObject({New Newtonsoft.Json.Linq.JProperty("h", "hello"), New Newtonsoft.Json.Linq.JProperty("vfwebqq", vfwebqq)})
            Get_Friend_Info.Add("r", Json_Send.ToString)
            With Http.HttpWebResponseUtility.CreatePostHttpResponse("http://s.web2.qq.com/api/get_user_friends2", Get_Friend_Info, -1, "", System.Text.Encoding.UTF8, Cookies, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3")
                Cookies.Add(.Cookies)
                Json_Friends_Str = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
            End With

            Dim Json_Root As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(Json_Friends_Str)
            If Json_Root("retcode") = "0" Then
                Dim Json_Result As Newtonsoft.Json.Linq.JObject = Json_Root("result")
                Dim Json_Friends As Newtonsoft.Json.Linq.JArray = Json_Result("friends")
                Dim Json_Categories As Newtonsoft.Json.Linq.JArray = Json_Result("categories")
                Dim Json_Info As Newtonsoft.Json.Linq.JArray = Json_Result("info")

                Dim Categories As New List(Of QQ_Friend_Categorie)
                Dim Default_Categorie As New QQ_Friend_Categorie With {.Index = 0, .Name = "My friends", .Sort = "0"}
                Categories.Add(Default_Categorie)
                For Each categ As Newtonsoft.Json.Linq.JObject In Json_Categories.Children
                    Dim New_careg As New QQ_Friend_Categorie With {.Index = categ("index"), .Name = categ("name"), .Sort = categ("sort")}
                    Categories.Add(New_careg)
                Next

                For i As Integer = 0 To Json_Friends.Count - 1
                    For Each j As QQ_Friend_Categorie In Categories.ToArray
                        If j.Index = Json_Friends(i)("categories") Then
                            j.Friends.Add(Json_Info(i)("uin"), Json_Info(i)("nick"))
                        End If
                    Next
                Next

                Dim r As New QQ_Friend_List
                r._str = Json_Friends_Str
                r.Catagories.AddRange(Categories)
                _Friend_List = r
                Return True
            Else
                Return False
            End If
        Catch ex As Exception
            Throw ex
            Return False
        End Try
    End Function

    Public Function Grab_Group_list() As Boolean
        Dim r As New List(Of QQ_Group)
        Try
            Dim Json_Group As String
            Dim Get_Group_Info As IDictionary(Of String, String) = New Dictionary(Of String, String)()
            Get_Group_Info.Add("r", "{" + Chr(34) + "vfwebqq" + Chr(34) + ":" + Chr(34) + vfwebqq + Chr(34) + "}")
            Get_Group_Info.Add("psessionid", psessionid)
            Get_Group_Info.Add("clientid", Clientid)
            With Http.HttpWebResponseUtility.CreatePostHttpResponse("http://s.web2.qq.com/api/get_group_name_list_mask2", Get_Group_Info, -1, "", System.Text.Encoding.UTF8, Cookies, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3")
                Json_Group = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
            End With

            Dim Json_Root As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(Json_Group)
            If Json_Root("retcode") = "0" Then
                Dim Json_Result As Newtonsoft.Json.Linq.JObject = Json_Root("result")
                Dim Json_gnamelist As Newtonsoft.Json.Linq.JArray = Json_Result("gnamelist")
                r.Clear()
                For Each group_item In Json_gnamelist.Children
                    r.Add(New QQ_Group(group_item("name"), group_item("gid"), group_item("code")))
                Next
                _Group_List = New QQ_Group_List(r.ToArray())
                Return True
            Else
                Return False
            End If
        Catch ex As Exception
            Throw ex
            Return False
        End Try
    End Function

    Public Shared Function Get_Face(ByVal Face As Integer) As Newtonsoft.Json.Linq.JArray
        Return New Newtonsoft.Json.Linq.JArray(New Newtonsoft.Json.Linq.JValue("face"), New Newtonsoft.Json.Linq.JValue(Face))
    End Function

    Public Shared Function Get_QQ_Font(Optional ByVal Name As String = "宋体", Optional ByVal Size As String = "10", Optional ByVal Color As String = "993366", Optional ByVal Style0 As Integer = 0, Optional ByVal Style1 As Integer = 0, Optional ByVal Style2 As Integer = 0) As Newtonsoft.Json.Linq.JArray
        Dim Json_Font As New Newtonsoft.Json.Linq.JArray
        Json_Font.Add(New Newtonsoft.Json.Linq.JValue("font"))
        Json_Font.Add(New Newtonsoft.Json.Linq.JObject({New Newtonsoft.Json.Linq.JProperty("name", Name),
                                                       New Newtonsoft.Json.Linq.JProperty("size", Size),
                                                       New Newtonsoft.Json.Linq.JProperty("color", Color),
                                                       New Newtonsoft.Json.Linq.JProperty("style", New Newtonsoft.Json.Linq.JArray({New Newtonsoft.Json.Linq.JValue(Style0),
                                                                                                                                   New Newtonsoft.Json.Linq.JValue(Style1),
                                                                                                                                   New Newtonsoft.Json.Linq.JValue(Style2)}))}))
        Return Json_Font
    End Function

    Public Function Post_Complex_MSG(ByVal target As String, ByVal Messages As Newtonsoft.Json.Linq.JToken(), Optional ByVal IsGroup As Boolean = False, Optional ByVal MessageId As Integer = 3180000, Optional Font As Newtonsoft.Json.Linq.JArray = Nothing) As String
        Dim s As String

        Dim Json_Root As New Newtonsoft.Json.Linq.JObject
        Dim Json_Content As New Newtonsoft.Json.Linq.JArray
        For Each i As Newtonsoft.Json.Linq.JToken In Messages
            Json_Content.Add(i)
        Next
        If Font Is Nothing Then Json_Content.Add(Get_QQ_Font()) Else Json_Content.Add(Font)
        Json_Root("content") = Json_Content.ToString
        If IsGroup Then Json_Root("group_uin") = target Else Json_Root("to") = target
        Json_Root("clientid") = Clientid
        Json_Root("psessionid") = psessionid
        Json_Root("msg_id") = MessageId
        s = Json_Root.ToString
        s = System.Web.HttpUtility.UrlEncode(s, Text.Encoding.UTF8)
        Dim Rec_Info As IDictionary(Of String, String) = New Dictionary(Of String, String)()
        Rec_Info.Add("r", s)
        Rec_Info.Add("psessionid", psessionid)
        Rec_Info.Add("clientid", Clientid)

        Dim Api As String
        If IsGroup Then Api = "http://d.web2.qq.com/channel/send_qun_msg2" Else Api = "http://d.web2.qq.com/channel/send_buddy_msg2"
        With Http.HttpWebResponseUtility.CreatePostHttpResponse(Api, Rec_Info, -1, "", System.Text.Encoding.UTF8, Cookies, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3")
            Return New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
        End With
        Exit Function
    End Function

    Public Function Post_Text_MSG(ByVal target As String, ByVal Message As String, Optional ByVal IsGroup As Boolean = False, Optional ByVal MessageId As Integer = 3180000) As String
        Return Post_Complex_MSG(target, {New Newtonsoft.Json.Linq.JValue(Message)}, IsGroup, MessageId)
    End Function

    'Send a Message(Does not block the recieving)
    Public Sub Send_Complex_Message(ByVal target As String, ByVal Messages As Newtonsoft.Json.Linq.JToken(), Optional ByVal IsGroup As Boolean = False, Optional ByVal MessageId As Integer = 3180000)
        Dim Sending_Thread As New Threading.Thread(Sub()
                                                       Dim r = Post_Complex_MSG(target, Messages, IsGroup, MessageId)
                                                       RaiseEvent On_Finish_Sending(r, target, Messages, IsGroup, MessageId)
                                                   End Sub)
        Sending_Thread.Start()
    End Sub

    'Send a Text Message(Does not block the recieving)
    Public Sub Send_Text_Message(ByVal target As String, ByVal Message As String, Optional ByVal IsGroup As Boolean = False, Optional ByVal MessageId As Integer = 3180000)
        Dim Sending_Thread As New Threading.Thread(Sub()
                                                       Dim r = Post_Complex_MSG(target, {New Newtonsoft.Json.Linq.JValue(Message)}, IsGroup, MessageId)
                                                       RaiseEvent On_Finish_Sending(r, target, {New Newtonsoft.Json.Linq.JValue(Message)}, IsGroup, MessageId)
                                                   End Sub)
        Sending_Thread.Start()
    End Sub

    Function Wait_For_MSG() As String
        Try
            Dim Rec_Info As IDictionary(Of String, String) = New Dictionary(Of String, String)()
            Rec_Info.Add("r", "{" + Chr(34) + "clientid" + Chr(34) + ":" + Chr(34) + Clientid + Chr(34) + "," + Chr(34) + "psessionid" + Chr(34) + ":" + Chr(34) + psessionid + Chr(34) + "," + Chr(34) + "key" + Chr(34) + ":0," + Chr(34) + "ids" + Chr(34) + ":[]}")
            Rec_Info.Add("psessionid", psessionid)
            Rec_Info.Add("clientid", Clientid)
            With Http.HttpWebResponseUtility.CreatePostHttpResponse("http://d.web2.qq.com/channel/poll2", Rec_Info, -1, "", System.Text.Encoding.UTF8, Cookies, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3")
                Return New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
            End With
        Catch ex As Exception
            Throw ex
            Return ""
        End Try
    End Function

    Sub Listening_Thread_Proc()  'The listening process
        Do  'A infinite loop
            Dim QQ_Messages As New List(Of QQ_Message)
            Dim msg As String = Wait_For_MSG() 'Wait for the Message and them recieve it
            If msg <> "" Then
                'Start analysing this msg
                Dim Json_Root As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(msg) 'Decode the message

                If Json_Root("retcode").ToString = "0" Then 'If there's an error
                    'No error
                    Dim Json_Results As Newtonsoft.Json.Linq.JArray = Json_Root("result")
                    For Each Json_Result As Newtonsoft.Json.Linq.JToken In Json_Results.Children 'Process each message
                        Dim Json_Value As Newtonsoft.Json.Linq.JObject = Json_Result("value")    'Grab its value
                        Select Case Json_Result("poll_type").ToString                            'Determine its type
                            Case "message"        'message from friend
                                QQ_Messages.Add(Analyse_QQMSG(Json_Value))
                            Case "group_message"  'message from group
                                QQ_Messages.Add(Analyse_QQMSG(Json_Value, True))
                            Case Else
                                RaiseEvent Ext_Poll_Proc(Json_Result) 'other poll message 
                        End Select
                    Next
                Else
                    RaiseEvent On_Error(msg)
                End If





                'Raise On_QQ_Message_Arrive event
                If QQ_Messages.Count > 0 Then
                    For Each m As QQ_Message In QQ_Messages.ToArray
                        If CMD_Head <> "" Then 'Whether the command mode
                            If m.GetString().ToLower.StartsWith(CMD_Head + ".") Then
                                'Costum command
                                Dim cmd As String = m.GetString.Replace(vbLf, "").Replace(vbCr, "").Substring(CMD_Head.Length + 1).Split({" "c}, StringSplitOptions.RemoveEmptyEntries)(0)
                                Dim param As String = ""

                                Dim p As String = m.GetString.Substring(CMD_Head.Length + 1).Trim.Substring(cmd.Length)
                                Dim splited_cmd As String() = p.Split({" "c}, StringSplitOptions.RemoveEmptyEntries)

                                For Each i As String In splited_cmd
                                    param += i.Trim + " "
                                Next

                                param = param.Trim

                                If m.From_Group Then
                                    RaiseEvent On_Command_Arrive(m.send_uin, m.from_uin, cmd, param)
                                Else
                                    RaiseEvent On_Command_Arrive(m.from_uin, 0, cmd, param)
                                End If
                            Else  'Normal message
                                QQ_Message_Arrive(m)
                            End If
                        Else
                            QQ_Message_Arrive(m)
                        End If
                    Next
                End If
            End If
        Loop
    End Sub

    Sub QQ_Message_Arrive(Message As QQ_Message)
        Dim Message_Threading As New Threading.Thread(Sub()
                                                          RaiseEvent On_QQ_Message_Arrive(Message)
                                                      End Sub)
        Message_Threading.Start()
    End Sub

    Function Analyse_QQMSG(ByVal Json_Value As Newtonsoft.Json.Linq.JObject, Optional ByVal From_Group As Boolean = False) As QQ_Message
        Dim QQMSG As New QQ_Message With {.msg_id = Json_Value("msg_id"),
                                          .from_uin = Json_Value("from_uin"),
                                          .to_uin = Json_Value("to_uin"),
                                          .msg_id2 = Json_Value("msg_id2"),
                                          .msg_type = Json_Value("msg_type"),
                                          .reply_ip = Json_Value("reply_ip"),
                                          .time = Json_Value("time"),
                                          .From_Group = From_Group}

        If QQMSG.From_Group Then
            QQMSG.group_code = Json_Value("group_code")
            QQMSG.send_uin = Json_Value("send_uin")
            QQMSG.seq = Json_Value("seq")
            QQMSG.info_seq = Json_Value("info_seq")
        End If

        Dim QQFont As New QQ_Font
        Dim Content_Array As Newtonsoft.Json.Linq.JArray = Json_Value("content")
        For Each Content_Item As Newtonsoft.Json.Linq.JToken In Content_Array.Children
            Select Case Content_Item.GetType.ToString
                Case GetType(Newtonsoft.Json.Linq.JArray).ToString
                    Select Case CType(Content_Item, Newtonsoft.Json.Linq.JArray)(0)
                        Case "offpic"
                            If CType(Content_Item, Newtonsoft.Json.Linq.JArray)(1)("success") = "1" Then QQMSG.Contents.Add(New QQ_Message_Content(CType(Content_Item, Newtonsoft.Json.Linq.JArray)(1)("file_path"), Content_Type.Picture))
                        Case "font"                                 'Font display part
                            Dim Json_Font As Newtonsoft.Json.Linq.JObject = CType(Content_Item, Newtonsoft.Json.Linq.JArray)(1)
                            QQFont.Size = Json_Font("size")
                            QQFont.Color = Json_Font("color")
                            QQFont.Style = Json_Font("style")
                            QQFont.Name = Json_Font("name")
                            QQMSG.Font = QQFont
                        Case "face"                                  'Face Part
                            Dim Face As String = CType(Content_Item, Newtonsoft.Json.Linq.JArray)(1)
                            QQMSG.Contents.Add(New QQ_Message_Content(Face, Content_Type.Face))
                    End Select
                Case GetType(Newtonsoft.Json.Linq.JValue).ToString   'Normal Text Part
                    QQMSG.Contents.Add(New QQ_Message_Content(Content_Item))
            End Select
        Next
        Return QQMSG
    End Function

    Public Sub Def_On_Command_Arrive(ByVal sender As String, ByVal group As String, ByVal cmd As String, ByVal Param As String)
        Dim targer As String
        If group = 0 Then targer = sender Else targer = group
        Select Case cmd.ToLower
            Case "admin"   'administrator logon
                If Param = CMD_Password And group = 0 Then
                    Administrator = sender
                    Send_Text_Message(targer, "You have become my master!", group)
                    RaiseEvent On_Log("Someone become the administrator: " + Friend_List.Get_Name_From_Id(sender))
                Else
                    Send_Text_Message(targer, "Wrong password!", group)
                    RaiseEvent On_Log("Someone input a incorrect administrator password: " + Friend_List.Get_Name_From_Id(sender))
                End If
            Case "stop"   'kill the robot
                If Administrator = sender Then
                    RaiseEvent On_Quit_Request()
                Else
                    RaiseEvent On_Log("A user try to stop the robot without authority: " + Friend_List.Get_Name_From_Id(sender))
                    Send_Text_Message(targer, "You don't have access to do this!", group)
                End If
            Case "about"  'show info about the AI platform
                Send_Text_Message(targer, "WebQQ AI Platform V0.99 Beta" + vbCrLf + "By 周行" + vbCrLf + "http://xiaocui.1x.net", group)
        End Select
    End Sub
End Class
