python---web微信开发

一:轮询,长轮询,WebSocket了解

轮询:

在前端,设置时间内,一直向后端发送请求。
例如:使用setInterval方法设置定时器,一秒向后端发送一次请求,去主动获取数据,进行更新
由于前端一直请求,后端压力太大。而且当没有数据更新,前端一直去请求,太浪费了,没必要。
代码简单

长轮询:

在轮询的基础上,加以改造。Http请求到来,若是不主动close或者return,则连接会一直存在。但是不要让这个时间太长,会占用太多资源
例如:当前端发送请求,后端拿到后,不去关闭,而是等待一段时间,在这段时间内若是有数据到达,立刻返回,否则直到等待时间结束。
然后返回给前端,前端马上又发起一次请求......
消息是实时获取。

WebSocket:

http是单向请求,客户端去服务端获取数据。服务端不能主动推送消息。
而websocket类似于socket,可以实现双向发送,
实现当数据更新,可以主动推送

二:web微信流程介绍

 三:微信登录开发

from django.shortcuts import render,HttpResponse
from bs4 import BeautifulSoup
import requests
import time,re,json

CTIME = None  #用于保存全局时间戳
QCODE = None  #当我们访问二维码时,会产生一个UUID,我们将其存放为全局
TIP = 1  #url中的一个参数tip,当其为1:代表我们还没有扫描二维码,当其为0:扫描了二维码

登录视图login,用于显示二维码

def login(request):
    global CTIME
    global QCODE
    CTIME = int(time.time())

    data = {
        'appid':'wx782c26e4c19acffb',
        'fun':'new',
        'lang':'zh_CN',
        '_':CTIME
    }

    response = requests.get(
        url="https://login.wx.qq.com/jslogin",
        params=data
    )

    pat_res = re.findall('uuid = "(.*)";',response.text)  #正则匹配UUID
    QCODE = pat_res[0]

    return render(request,"login.html",{'qcode':QCODE})

check_login用于检测登录状态:408未扫描,201扫描二维码但是未登录,200点击登录

def check_login(request):
    global TIP
    ret = {'code':408,'data':None}
    data = {
        'loginicon':"true",
        'uuid':QCODE,
        'tip':TIP,
        'r':'-577317906',
        '_':int(time.time())
    }
    r1 = requests.get(
        url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
        params=data
    )

    if 'window.code=408' in r1.text:
        print("无人扫描")
        return HttpResponse(json.dumps(ret))
    elif 'window.code=201' in r1.text:
        ret['code'] = 201
        pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[0]
        ret['data'] = pat_ret
        TIP = 0
        return HttpResponse(json.dumps(ret))
    elif 'window.code=200;' in r1.text:
        ret['code'] = 200
        redirect_url = re.findall('window.redirect_uri="(.*)";',r1.text)[0]
        reponse = requests.get(
            url=redirect_url+"&fun=new&version=v2"  #url不够完整,需要我们完善
        )
        # print(reponse.text) #<error><ret>0</ret><message></message><skey>@crypt_7358fe11_af06754907ad9c216768337d80cf0ce7</skey><wxsid>icUySQoySDi2OZFK</wxsid><wxuin>2821071261</wxuin><pass_ticket>IWScm1SE%2BGQ%2BNEaghUBCxbF3xPJSzqXUGTO6BYh3TBEGlw8Wa7qETkA9EEAUudYU</pass_ticket><isgrayscale>1</isgrayscale></error>
        soup = BeautifulSoup(reponse.text,"lxml")
        info_dict = {}
        for tag in soup.find("error").children:
            info_dict[tag.name]=tag.get_text()


        get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket='+info_dict['pass_ticket']
        get_user_info_form = {
            'BaseRequest':
            {
                'DeviceID':"e055319847811019",
                'Sid':info_dict['wxsid'],
                'Skey':info_dict['skey'],
                'Uin':info_dict['wxuin']
            }
        }


        reponse2 = requests.post(   #获取的是用户信息,最近联系人,公众号,自己信息
            url=get_user_info_url,
            json=get_user_info_form,    #注意这里使用的是json,post不允许传送字典
        )

        reponse2.encoding = "utf-8"
        print(reponse2.text)

        return HttpResponse("OK")
        '''
        新请求 GET 获取跳转地址redirect_uri
        https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?
        loginicon=true
        &uuid=QfsKELYXow==
        &tip=0
        &r=-613406501
        &_=1529621492415
        ---------------------------------------------------------
            window.code=200;
            window.redirect_uri="
                https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?
                    ticket=ASWg1dxC1oWVbJtZH8V-HhlB@qrticket_0
                    &uuid=QfsKELYXow==
                    &lang=zh_CN
                    &scan=1529621533";
                    
        新请求   GET    获取凭证pass_ticket    服务端开始设置了cookie,说明在后面的请求中需要携带cookie
        https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?
        ticket=ASWg1dxC1oWVbJtZH8V-HhlB@qrticket_0
        &uuid=QfsKELYXow==
        &lang=zh_CN
        &scan=1529621533
        &fun=new
        &version=v2
        -----------------------------------------------------------------
            <error>
                <ret>0</ret>
                <message></message>
                <skey>@crypt_7358fe11_ea821d506c39f7d75a3e83b4233caab4</skey>
                <wxsid>qUJZlkBIWQ0130QI</wxsid>
                <wxuin>2821071261</wxuin>
                <pass_ticket>xNiKeCBgFkMBfEK8oOK3Gp9qj%2F1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ</pass_ticket>
                <isgrayscale>1</isgrayscale>
            </error>
        
        新请求:获取用户所有信息,最近联系人和公众号    POST    需要携带数据,数据来自于上面凭证中
        https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?
        r=-613135321
        &pass_ticket=xNiKeCBgFkMBfEK8oOK3Gp9qj%252F1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ
        
        数据
        {
            BaseRequest:
            {
                DeviceID:"e055319847811019"
                Sid:"pY7nfHplUAsBOINz"
                Skey:"@crypt_7358fe11_e0ae163bd19650bea336df66837e9f7a"
                Uin:"2821071261"
            }
        }
        --------------------------------------------------------------------
        {
            "BaseResponse": {
            "Ret": 0,
            "ErrMsg": ""
            }
            ,
            "Count": 9,
            "ContactList": [{
            "Uin": 0,
            "UserName": "filehelper",
            "NickName": "文件传输助手",
            "HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=660872310&username=filehelper&skey=@crypt_7358fe11_ea821d506c39f7d75a3e83b4233caab4",
            "ContactFlag": 2,
            "MemberCount": 0,
            "MemberList": [],
            "RemarkName": "",
            "HideInputBarFlag": 0,
            "Sex": 0,
            "Signature": "",
            "VerifyFlag": 0,
            "OwnerUin": 0,
            "PYInitial": "WJCSZS",
            "PYQuanPin": "wenjianchuanshuzhushou",
            "RemarkPYInitial": "",
            "RemarkPYQuanPin": "",
            "StarFriend": 0,
            "AppAccountFlag": 0,
            "Statues": 0,
            "AttrStatus": 0,
            "Province": "",
            "City": "",
            "Alias": "",
            "SnsFlag": 0,
            "UniFriend": 0,
            "DisplayName": "",
            "ChatRoomId": 0,
            "KeyWord": "fil",
            "EncryChatRoomId": "",
            "IsOwner": 0
            },还有其他的]
        }

        新请求
        https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?
        r=-613135321
        &pass_ticket=xNiKeCBgFkMBfEK8oOK3Gp9qj%252F1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ
        
        
        新请求 GET 获取所有联系人和公众号
        https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?
        lang=zh_CN
        &pass_ticket=gWbCT8vTjFeFKXDvfJZ6DtMtHo5d8zzhtLgLoybILn7eeTNSMI4BErA7e9otuPXQ
        &r=1529642330650
        &seq=0
        &skey=@crypt_7358fe11_9dc260b8cffb962a3e475ca50e7813c9
        -----------------------------------------------------------------------------
        {
        "BaseResponse": {
        "Ret": 0,
        "ErrMsg": ""
        }
        ,
        "MemberCount": 162,
        "MemberList": [{
            "Uin": 0,
            "UserName": "@39ef4d4197e9a7388e41fc9de150b3e28bf125082f1e442822814dec4803c6a0",
            "NickName": "宁静致远",
            "HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@39ef4d4197e9a7388e41fc9de150b3e28bf125082f1e442822814dec4803c6a0&skey=@crypt_7358fe11_9dc260b8cffb962a3e475ca50e7813c9",
            "ContactFlag": 1,
            "MemberCount": 0,
            "MemberList": [],
            "RemarkName": "",
            "HideInputBarFlag": 0,
            "Sex": 1,
            "Signature": "凶巴巴呛贝贝",
            "VerifyFlag": 0,
            "OwnerUin": 0,
            "PYInitial": "NJZY",
            "PYQuanPin": "ningjingzhiyuan",
            "RemarkPYInitial": "",
            "RemarkPYQuanPin": "",
            "StarFriend": 0,
            "AppAccountFlag": 0,
            "Statues": 0,
            "AttrStatus": 4197,
            "Province": "河南",
            "City": "郑州",
            "Alias": "",
            "SnsFlag": 17,
            "UniFriend": 0,
            "DisplayName": "",
            "ChatRoomId": 0,
            "KeyWord": "",
            "EncryChatRoomId": "",
            "IsOwner": 0
            },
            还有其他
        ]
        '''
各个url详细请求

前端代码:显示二维码和头像

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<img id="qrcode" style=" 340px;height: 340px;" src="https://login.weixin.qq.com/qrcode/{{ qcode }}" alt="">
</body>
</html>
<script src="/static/jquery.js"></script>
<script>
    $(function(){
        checkLogin();
    })

    function checkLogin() {
        $.ajax({
            url:'/check-login.html',
            type:'GET',
            dataType:"json",
            success:function(data){
                console.log(data.code);
                if (data.code==408){
                    checkLogin();
                }
                else if(data.code==201){
                    $("#qrcode").attr('src',data.data)
                    checkLogin();
                }
            }
        })
    }
</script>

测试返回的最近联系人和公众号信息

user_dict = {}


for item in user_dict.items():
    print(item)

for item in user_dict['ContactList']:   #最近联系人
    print(item['PYQuanPin'],item['NickName'])


for item in user_dict['MPSubscribeMsgList']:    #公众号和推送消息
    print(item['UserName'],item['NickName'])
    for item2 in item['MPArticleList']:
        print(item2['Title'],item2['Cover'],item2['Digest'],item2['Url'])
最近联系人和公众号
('ClientVersion', 637929271)
('GrayScale', 1)
('Count', 10)
('SystemTime', 1529661118)
('MPSubscribeMsgList:公众号列表,含有文章推送等信息', [{'UserName': '@393d71e59f81ac2feca148e8e269c0df', 'MPArticleList': [{'Title': '', 'Digest': '', 'Url': '', 'Cover': '图片'}, ], 'MPArticleCount': 2, 'Time': 1529651105, 'NickName': '人工智能头条'},])
('ChatSet', 'filehelper,@@7c7137978e7349eac97453fa2adc290df295eaba3e7981e07ff85111f94a403c,weixin,@0fdf14d27dc0b2d34d013329ec498aae6284dbc340bdfbd8741227a72b1b3fa4,@393d71e59f81ac2feca148e8e269c0df,@@4d7d0c68e8445a6d69a5e3a2415c57c8f46858724cfdef8618b5790094e5de37,@@6b45638d8a8394a5bea103bd55ef49ce69dad73b8eda94dd5f63a126ec0e6ee4,@@d6c45082c0686e0cef729f3cb20db704b381b2aef67fe0a8a82151869220c8ef,@@03e7d8c59c30bb81dc0f2dc683b8e7a6f4a707f2aac6655c6e5036c349a96fe3,@02bf3be3c826bc38d4461d3ee52704e8,')
('MPSubscribeMsgCount:最近推送的公众号数目', 2)
('BaseResponse', {'ErrMsg': '', 'Ret': 0})
('SKey', '@crypt_7358fe11_08012eadffc70f5c3189f802236830be')
('ClickReportInterval', 600000)
('InviteStartCount', 40)
('User:用户自己的信息', {'VerifyFlag': 0, 'HeadImgFlag': 1, 'Uin': 2821071261, 'NickName': '宁静致远', 'AppAccountFlag': 0, 'UserName': '@c959c389ab390d9f71d3f528f5a4ee1e81d6c8cd4aaf48d8b1f0077073660c5c', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=1753775271&username=@c959c389ab390d9f71d3f528f5a4ee1e81d6c8cd4aaf48d8b1f0077073660c5c&skey=@crypt_7358fe11_08012eadffc70f5c3189f802236830be', 'ContactFlag': 0, 'RemarkPYInitial': '', 'SnsFlag': 17, 'PYQuanPin': '', 'WebWxPluginSwitch': 0, 'HideInputBarFlag': 0, 'RemarkPYQuanPin': '', 'Signature': '凶巴巴呛贝贝', 'Sex': 1, 'StarFriend': 0, 'PYInitial': '', 'RemarkName': ''})
('ContactList:最近联系人信息', [
 {'VerifyFlag': 0, 'Uin': 0, 'Signature': '', 'AppAccountFlag': 0, 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=660872310&username=filehelper&skey=@crypt_7358fe11_08012eadffc70f5c3189f802236830be', 'PYInitial': 'WJCSZS', 'Province': '', 'PYQuanPin': 'wenjianchuanshuzhushou', 'DisplayName': '', 'RemarkName': '', 'IsOwner': 0, 'Sex': 0, 'EncryChatRoomId': '', 'KeyWord': 'fil', 'City': '', 'ChatRoomId': 0, 'RemarkPYQuanPin': '', 'Alias': '', 'UniFriend': 0, 'UserName': 'filehelper', 'MemberCount': 0, 'ContactFlag': 2, 'RemarkPYInitial': '', 'Statues': 0, 'AttrStatus': 0, 'SnsFlag': 0, 'HideInputBarFlag': 0, 'NickName': '文件传输助手', 'OwnerUin': 0, 'StarFriend': 0, 'MemberList': []},])
('SyncKey', {'List': [{'Key': 1, 'Val': 677540039}, {'Key': 2, 'Val': 677540219}, {'Key': 3, 'Val': 677540036}, {'Key': 1000, 'Val': 1529658962}], 'Count': 4})
相关数据打印(格式)

 四:显示最近联系人和公众号

视图所有代码:对于上面是有所修改的

from django.shortcuts import render,HttpResponse,redirect
from bs4 import BeautifulSoup
import requests
import time,re,json

CTIME = None
QCODE = None
TIP = 1
TICKET_DICT = {}    #保存凭证信息
ALL_COOKIE_DICT = {}


# Create your views here.
def login(request):
    global CTIME
    global QCODE
    CTIME = int(time.time()*1000)

    data = {
        'appid':'wx782c26e4c19acffb',
        'fun':'new',
        'lang':'zh_CN',
        '_':CTIME
    }

    response = requests.get(
        url="https://login.wx.qq.com/jslogin",
        params=data
    )

    pat_res = re.findall('uuid = "(.*)";',response.text)
    QCODE = pat_res[0]

    return render(request,"login.html",{'qcode':QCODE})

def check_login(request):
    global TIP
    ret = {'code':408,'data':None}
    data = {
        'loginicon':"true",
        'uuid':QCODE,
        'tip':TIP,
        'r':'-577317906',
        '_':int(time.time())
    }
    r1 = requests.get(
        url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
        params=data
    )

    if 'window.code=408' in r1.text:
        print("无人扫描")
        return HttpResponse(json.dumps(ret))
    elif 'window.code=201' in r1.text:
        ret['code'] = 201
        pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[0]
        ret['data'] = pat_ret
        TIP = 0
        return HttpResponse(json.dumps(ret))
    elif 'window.code=200;' in r1.text:
        ret['code'] = 200
        redirect_url = re.findall('window.redirect_uri="(.*)";', r1.text)[0]
        reponse = requests.get(     #获取凭证,这里也开始设置cookie了,所以我们在这里向后需要记录cookie
            url=redirect_url + "&fun=new&version=v2"  # url不够完整,需要我们完善
        )
        ALL_COOKIE_DICT.update(reponse.cookies)

        soup = BeautifulSoup(reponse.text, "lxml")
        info_dict = {}
        for tag in soup.find("error").children:
            info_dict[tag.name] = tag.get_text()

        global TICKET_DICT
        TICKET_DICT.update(info_dict)

        ret['code']=200

        return HttpResponse(json.dumps(ret))


def user(request):
    get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
        'pass_ticket']
    get_user_info_form = {
        'BaseRequest':
            {
                'DeviceID': "e055319847811019",
                'Sid': TICKET_DICT['wxsid'],
                'Skey': TICKET_DICT['skey'],
                'Uin': TICKET_DICT['wxuin']
            }
    }

    reponse2 = requests.post(  # 获取的是用户信息,几个联系人,公众号,自己信息
        url=get_user_info_url,
        json=get_user_info_form,  # 注意这里使用的是json,post不允许传送字典
    )
    ALL_COOKIE_DICT.update(reponse2.cookies)

    reponse2.encoding = "utf-8"

    user_info_dict = json.loads(reponse2.text)  # 获取的是用户信息,几个联系人,公众号,自己信息

    return render(request, "user.html", {'user_info_dict': user_info_dict})
views修改后的代码,主要是将凭证放入全局字典

视图方法user去获取最近联系人

注意:我们将上面的凭证保存到了全局变量中TICKET_DICT方便查询使用

def user(request): get_user_info_url
= 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[ 'pass_ticket'] get_user_info_form = { 'BaseRequest': { 'DeviceID': "e055319847811019", 'Sid': TICKET_DICT['wxsid'], 'Skey': TICKET_DICT['skey'], 'Uin': TICKET_DICT['wxuin'] } } reponse2 = requests.post( # 获取的是用户信息,几个联系人,公众号,自己信息 url=get_user_info_url, json=get_user_info_form, # 注意这里使用的是json,post不允许传送字典 ) ALL_COOKIE_DICT.update(reponse2.cookies) reponse2.encoding = "utf-8" user_info_dict = json.loads(reponse2.text) # 获取的是用户信息,几个联系人,公众号,自己信息 return render(request, "user.html", {'user_info_dict': user_info_dict})

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <h3>最近联系人</h3>
        <ul>
            {% for item in user_info_dict.ContactList %}
                <li>{{ item.NickName }}</li>
            {% endfor %}
        </ul>
        <a href="/contact-list.html">获取更多联系人</a>
    </div>
    <div>
        <h3>微信公众号</h3>
        <div>
            {% for item in user_info_dict.MPSubscribeMsgList %}
                <h4>{{ item.NickName }}</h4>
                <ul>
                    {% for item2 in item.MPArticleList %}
                        <li>
                            <a href="{{ item2.Url }}">
                                {{ item2.Title }}
                            </a>
                        </li>
                    {% endfor %}
                </ul>
            {% endfor %}
        </div>
    </div>
</body>
</html>

五:显示所有联系人

视图所有的修改:主要在设置一个全局字典存放网站cookie,注意这里是需要携带cookie的,而cookie是在我们点击登录后,服务器开始设置的,我们需要去获取自那时以后的所有cookie

from django.shortcuts import render,HttpResponse,redirect
from bs4 import BeautifulSoup
import requests
import time,re,json

CTIME = None
QCODE = None
TIP = 1
TICKET_DICT = {}    #保存凭证信息
ALL_COOKIE_DICT = {}


# Create your views here.
def login(request):
    global CTIME
    global QCODE
    CTIME = int(time.time()*1000)

    data = {
        'appid':'wx782c26e4c19acffb',
        'fun':'new',
        'lang':'zh_CN',
        '_':CTIME
    }

    response = requests.get(
        url="https://login.wx.qq.com/jslogin",
        params=data
    )

    pat_res = re.findall('uuid = "(.*)";',response.text)
    QCODE = pat_res[0]

    return render(request,"login.html",{'qcode':QCODE})

def check_login(request):
    global TIP
    ret = {'code':408,'data':None}
    data = {
        'loginicon':"true",
        'uuid':QCODE,
        'tip':TIP,
        'r':'-577317906',
        '_':int(time.time())
    }
    r1 = requests.get(
        url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
        params=data
    )

    if 'window.code=408' in r1.text:
        print("无人扫描")
        return HttpResponse(json.dumps(ret))
    elif 'window.code=201' in r1.text:
        ret['code'] = 201
        pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[0]
        ret['data'] = pat_ret
        TIP = 0
        return HttpResponse(json.dumps(ret))
    elif 'window.code=200;' in r1.text:
        ret['code'] = 200
        redirect_url = re.findall('window.redirect_uri="(.*)";', r1.text)[0]
        reponse = requests.get(     #获取凭证,这里也开始设置cookie了,所以我们在这里向后需要记录cookie
            url=redirect_url + "&fun=new&version=v2"  # url不够完整,需要我们完善
        )
        ALL_COOKIE_DICT.update(reponse.cookies)

        soup = BeautifulSoup(reponse.text, "lxml")
        info_dict = {}
        for tag in soup.find("error").children:
            info_dict[tag.name] = tag.get_text()

        global TICKET_DICT
        TICKET_DICT.update(info_dict)

        ret['code']=200

        return HttpResponse(json.dumps(ret))


def user(request):
    get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
        'pass_ticket']
    get_user_info_form = {
        'BaseRequest':
            {
                'DeviceID': "e055319847811019",
                'Sid': TICKET_DICT['wxsid'],
                'Skey': TICKET_DICT['skey'],
                'Uin': TICKET_DICT['wxuin']
            }
    }

    reponse2 = requests.post(  # 获取的是用户信息,几个联系人,公众号,自己信息
        url=get_user_info_url,
        json=get_user_info_form,  # 注意这里使用的是json,post不允许传送字典
    )
    ALL_COOKIE_DICT.update(reponse2.cookies)

    reponse2.encoding = "utf-8"

    user_info_dict = json.loads(reponse2.text)  # 获取的是用户信息,几个联系人,公众号,自己信息

    return render(request, "user.html", {'user_info_dict': user_info_dict})


def contact_list(request):
    get_all_user_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%s&seq=0&skey=%s' % (TICKET_DICT['pass_ticket'],int(time.time()*1000),TICKET_DICT['skey'])

    reponse = requests.get(
        url=get_all_user_url,   #这里需要用到cookie
        cookies=ALL_COOKIE_DICT
    )

    reponse.encoding = "utf-8"
    contact_info_list = json.loads(reponse.text)


    return render(request,"contact_info.html",{'contact_info_list':contact_info_list})
所有视图代码,主要修改在ALL_COOKIE_DICT 存放cookie

不携带cookie情况:

视图方法:contact_list

def contact_list(request):
    get_all_user_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%s&seq=0&skey=%s' % (TICKET_DICT['pass_ticket'],int(time.time()*1000),TICKET_DICT['skey'])

    reponse = requests.get(
        url=get_all_user_url,   #这里需要用到cookie
        cookies=ALL_COOKIE_DICT
    )

    reponse.encoding = "utf-8"
    contact_info_list = json.loads(reponse.text)


    return render(request,"contact_info.html",{'contact_info_list':contact_info_list})

 前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <h3>全部联系人列表</h3>
        <ul>
            {% for item in contact_info_list.MemberList %}
            <li>{{ item.NickName }}</li>
            {% endfor %}
        </ul>
    </div>
</body>
</html>

 六:模拟发送信息

发送信息的url:

https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=l8yQcPtEelgrNY6fSnMf72i%252BoP10LCLTdmjnFgEQOCK9n7401krRfKnc0xMbNweJ

POST传递数据内容:

{
"BaseRequest":  #这里数据存放在全局凭证中 {"Uin":2821071261, "Sid":"xU10r+19IxmPU8Fb", "Skey":"@crypt_7358fe11_5fc69b570e562f35f5e96aa1039d83aa", "DeviceID":"e308142734343946" }, "Msg":  #发送的数据信息 {"Type":1,  #文本信息 "Content":"参数",  #发送的数据 "FromUserName":"@c63e475396e438ef81d9825832217c06e4cc302269db05b7eae7e2980de2d56d",  #我的username "ToUserName":"@94bcc0a92c726c0e2639ffa59618549d222bee0107150515cf247dc8d45f8144",  #发给谁username "LocalID":"15296541823070630",  #和时间戳一致 "ClientMsgId":"15296541823070630"  #时间戳 }, "Scene":0 }
{"BaseRequest":
     {"Uin":2821071261,
      "Sid":"xU10r+19IxmPU8Fb",
      "Skey":"@crypt_7358fe11_5fc69b570e562f35f5e96aa1039d83aa",
      "DeviceID":"e149192355196085"  #可变的设备ID
      },
 "Msg":
     {"Type":1,  
      "Content":"哈哈哈",  #发送内容改变了
      "FromUserName":"@c63e475396e438ef81d9825832217c06e4cc302269db05b7eae7e2980de2d56d",
      "ToUserName":"@94bcc0a92c726c0e2639ffa59618549d222bee0107150515cf247dc8d45f8144",
      "LocalID":"15296546798310239",  #同时间戳一致
      "ClientMsgId":"15296546798310239"  #时间戳改变了
      },
"Scene":0
 }

 修改前端contact_info.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/nifty.min.css" rel="stylesheet">
    <link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet">
    <link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet">
    <link href="/static/plugins/pace/pace.min.css" rel="stylesheet">
    <script src="/static/js/jquery-2.2.4.min.js"></script>
    <script>
    $(function(){
        $(".list-group-item").click(function(){
            $(".list-unstyled").empty();
            $(this).siblings().removeClass("active");
            $(this).addClass("active");
            var NickName = $(this).first().text().trim();
            $(".panel-title").text(NickName);
        })
    })
    </script>
    <script src="/static/plugins/pace/pace.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <script src="/static/js/nifty.min.js"></script>
    <script src="/static/js/demo/nifty-demo.min.js"></script>
    <script src="/static/plugins/flot-charts/jquery.flot.min.js"></script>
    <script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script>
    <script src="/static/plugins/gauge-js/gauge.min.js"></script>
    <script src="/static/plugins/skycons/skycons.min.js"></script>
    <script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script>
    <script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect  aside-bright mainnav-sm aside-right aside-in">
    <div class="boxed">
        <div id="content-container">
            <div class="row">
                <div class="col-md-8 col-lg-8 col-sm-8">

            <!--Chat widget-->
            <!--===================================================-->
            <div class="panel" style="height: 640px">
                <!--Heading-->
                <div class="panel-heading">
                    <h3 class="panel-title">Chat</h3>
                </div>

                <!--Widget body-->
                <div style="height:510px;padding-top:0px;" class="widget-body">
                    <div class="nano">
                        <div class="nano-content pad-all">
                            <ul class="list-unstyled media-block">
                                <li class="mar-btm">
                                    <div class="media-left">
                                        <img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture">
                                    </div>
                                    <div class="media-body pad-hor">
                                        <div class="speech">
                                            <a href="#" class="media-heading">Aaron Chavez</a>
                                            <p>Hello Lucy, how can I help you today ?</p>
                                            <p class="speech-time">
                                            <i class="demo-pli-clock icon-fw"></i>09:23AM
                                            </p>
                                        </div>
                                    </div>
                                </li>
                                <li class="mar-btm">
                                    <div class="media-right">
                                        <img src="img/profile-photos/8.png" class="img-circle img-sm" alt="Profile Picture">
                                    </div>
                                    <div class="media-body pad-hor speech-right">
                                        <div class="speech">
                                            <a href="#" class="media-heading">Lucy Doe</a>
                                            <p>Hi, I want to buy a new shoes.</p>
                                            <p class="speech-time">
                                                <i class="demo-pli-clock icon-fw"></i> 09:23AM
                                            </p>
                                        </div>
                                    </div>
                                </li>
                            </ul>
                        </div>
                    </div>

                    <!--Widget footer-->
                    <div class="panel-footer" style="height: 90px;">
                        <div class="row">
                            <div class="col-xs-9">
                                <input type="text" placeholder="Enter your text" class="form-control chat-input">
                            </div>
                            <div class="col-xs-3">
                                <button class="btn btn-primary btn-block" onclick="sendMsg(this);" type="submit">Send</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!--===================================================-->
            <!--Chat widget-->

        </div>
                <div class="col-md-4 col-lg-4 col-sm-4">
                    <aside id="aside-container">
                <div id="aside">
                    <div class="nano has-scrollbar">
                        <div class="nano-content" tabindex="0" style="right: -17px;">

                            <!--Nav tabs-->
                            <!--================================-->
                            <ul class="nav nav-tabs nav-justified">
                                <li class="active">
                                    <a href="#demo-asd-tab-1" data-toggle="tab">
                                        <i class="demo-pli-speech-bubble-7"></i>
                                    </a>
                                </li>
                            </ul>
                            <!--================================-->
                            <!--End nav tabs-->

                            <!-- Tabs Content -->
                            <!--================================-->
                            <div class="tab-content">
                                <div class="tab-pane fade in active" id="demo-asd-tab-1">
                                    <p class="pad-hor text-semibold text-main">
                                        <span class="pull-right badge badge-success">{{ contact_info_list.MemberCount }}</span> Friends
                                    </p>

                                    <!--Works-->
                                    <div class="list-group bg-trans">
                                        {% for item in contact_info_list.MemberList %}
                                            <a href="#" for="{{ item.UserName }}" class="list-group-item">
                                                <span class="badge badge-purple badge-icon badge-fw pull-left"></span> {{ item.NickName }}
                                            </a>
                                        {% endfor %}
                                    </div>
                                </div>
                            </div>
                        </div>
                    <div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div>
                </div>
            </aside>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

<script>
    function sendMsg(ths){
        var sel_tag = $(".list-group").find(".active")
        if(sel_tag.length==0){
            return false;
        }
        var msg = $(ths).parents(".panel-footer").find(".chat-input").val();
        var sendMsg={
            'ToUserName':sel_tag.attr("for"),
            'Type':1,
            'Content':msg,
            'csrfmiddlewaretoken':'{{ csrf_token }}'
        }

        $.ajax({
            url:"send-msg.html",
            data:sendMsg,
            type:"POST",
            dataType:"json",
            success:function(callback){
                if (callback.code==200){
                    var dt = new Date()
                    var now_time = dt.toLocaleString();

                    console.log(callback);
                    var li = '<li class="mar-btm"><div class="media-right"><img src="'+callback.headImgUrl+'" class="img-circle img-sm" alt="Profile Picture"></div>';
                    li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">'+callback.username+'</a>';
                    li += '<p>'+msg+'</p>';
                    li += '<p class="speech-time">';
                    li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;
                    li += '</p></div></div></li>';
                    $(ths).parents(".widget-body").find(".list-unstyled").append(li);
                    $(ths).parents(".panel-footer").find(".chat-input").val("");
                }
            }
        })
    }
</script>
前端代码使用ajax向后端传送

后端代码send_msg

注意:处理传送中文时,在requests模块有点麻烦,下面代码有写解决方法

def send_msg(request):
    ret = {
        'code':200,
        'error':'Send Success',
        'data':{}
    }

    recv_data = request.POST

    if not recv_data:
        ret['code']=400
        ret['data']="Send failure"
        return HttpResponse(json.dumps(ret))

    #数据整合
    Send_data={}
    Send_data['BaseRequest'] = {
        'DeviceID': "e055319847811019",
        'Sid': TICKET_DICT['wxsid'],
        'Skey': TICKET_DICT['skey'],
        'Uin': TICKET_DICT['wxuin']
    }

    Send_data['Msg'] = {
        'Type':recv_data.get("Type",1),
        'Content':recv_data.get("Content"),
        'FromUserName':USER_INIT_DICT['User']['UserName'],
        'ToUserName':recv_data.get("ToUserName"),
        'LocalID':int(time.time()*1000),
        'ClientMsgId':int(time.time()*1000),
    }
    Send_data['Scene']=0

    send_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=%s'%TICKET_DICT['pass_ticket']
    # reponse = requests.post(
    #     url=send_url,
    #     json=Send_data, #注意这里如果使用json,会将中文转换为Unicode
    #     cookies=ALL_COOKIE_DICT
    # )
    reponse = requests.post(
        url=send_url,
        #若是有中文,需要加上ensure_ascii=False,若是字符串中含有中文,request传递数据时,将中文转换为字节,无法为我们转换。需要我们提前使用encoding编码,直接传递字节,不让requests为我们转换
        #data可以是字典,字符串,字节,既然对于字典,字符串直接含有中文不正确,直接转字节传送,py3默认是utf-8,所以我们直接传送字节就可以
        data=bytes(json.dumps(Send_data,ensure_ascii=False),encoding="utf-8"), #注意这里如果使用json,会将中文转换为Unicode
        cookies=ALL_COOKIE_DICT  #携带cookie
    )

    reponse.encoding = "utf-8"

    ret_status = re.findall('"Ret": (.*),', reponse.text)[0]
    ret_error = re.findall('"ErrMsg": "(.*)"', reponse.text)[0]

    if int(ret_status) != 0:
        ret['code']=401
        ret['data']=ret_error
        return HttpResponse(json.dumps(ret))

    ret['username'] = USER_INIT_DICT['User']['NickName']
    ret['headImgUrl'] = 'https://wx.qq.com'+USER_INIT_DICT['User']['HeadImgUrl']

    return HttpResponse(json.dumps(ret))

 七:实现长轮询接收消息

先参考微信的实例:微信依靠两个url实现去后端获取数据

1.webwxsync:ajax使用POST,长轮询去获取发送的消息,和获取一个SyncCheckKey,下面检测是否有消息到了需要携带这个SyncCheckKey

https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=NOWucA2Et3xw0l8a&skey=@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&pass_ticket=9ZK9OoNzaLxkdRqVWghy7uGzWFvsIzXNWDjgeJqTskg3Bl08tQxAZ9t0hcEYOzmO

要传递的POST数据
{  
"BaseRequest":{  #都是已经获取的数据
"Uin":2821071261,
"Sid":"NOWucA2Et3xw0l8a",
"Skey":"@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853",
"DeviceID":"e937865997050874"
},
"SyncKey":{  #我们上一次的SyncKey值
"Count":9,"List":[{"Key":1,"Val":677540346},{"Key":2,"Val":677540435},{"Key":3,"Val":677540036},{"Key":11,"Val":677540181},{"Key":201,"Val":1529672373},{"Key":203,"Val":1529658601},{"Key":1000,"Val":1529658962},{"Key":1001,"Val":1529659033},{"Key":2001,"Val":1529480143}]
},
"rr":-664997335  #未知
}
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
}
,
"AddMsgCount": 0,
"AddMsgList": [],
"ModContactCount": 0,
"ModContactList": [],
"DelContactCount": 0,
"DelContactList": [],
"ModChatRoomMemberCount": 0,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": 0,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": 0,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": 0,
"Sex": 0,
"PersonalCard": 0,
"Alias": "",
"HeadImgUpdateFlag": 0,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": 0,
"SyncKey": {
"Count": 9,
"List": [
    {
    "Key": 1,
    "Val": 677540346
    }
    ,{
    "Key": 2,
    "Val": 677540423
    }
    ,{
    "Key": 3,
    "Val": 677540036
    }
    ,{
    "Key": 11,
    "Val": 677540181
    }
    ,{
    "Key": 201,
    "Val": 1529671726
    }
    ,{
    "Key": 203,
    "Val": 1529658601
    }
    ,{
    "Key": 1000,
    "Val": 1529658962
    }
    ,{
    "Key": 1001,
    "Val": 1529659033
    }
    ,{
    "Key": 2001,
    "Val": 1529480143
    }
]
}
,
"SKey": "",
"SyncCheckKey": {
"Count": 9,
"List": [
    {
    "Key": 1,
    "Val": 677540346
    }
    ,{
    "Key": 2,
    "Val": 677540423
    }
    ,{
    "Key": 3,
    "Val": 677540036
    }
    ,{
    "Key": 11,
    "Val": 677540181
    }
    ,{
    "Key": 201,
    "Val": 1529671726
    }
    ,{
    "Key": 203,
    "Val": 1529658601
    }
    ,{
    "Key": 1000,
    "Val": 1529658962
    }
    ,{
    "Key": 1001,
    "Val": 1529659033
    }
    ,{
    "Key": 2001,
    "Val": 1529480143
    }
]
}
}
获取的数据:SyncKey
2.synccheck:轮询请求,用于检测是否有人发消息过来。注意:当我们刚刚登陆时,是有一个初始的synckey的,可以在《相关数据打印(格式)》那里看到

https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1529672369137&skey=%40crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a&uin=2821071261&deviceid=e539832593408529&synckey=1_677540346%7C2_677540434%7C3_677540036%7C11_677540181%7C201_1529672366%7C203_1529658601%7C1000_1529658962%7C1001_1529659033%7C2001_1529480143&_=1529671539738
https:
//webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck? r=1529671730362 #时间戳 &skey=%40crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a &uin=2821071261 #和其他数据一起放在TICKET_DICT中 &deviceid=e065841963151848 #设备id &synckey= 1_677540346 1 677540346 #和上一个请求url中接收的数据一致 %7C | 2_677540423 2 677540423 %7C | 3_677540036 %7C 11_677540181%7C201_1529671726%7C203_1529658601%7C1000_1529658962%7C1001_1529659033%7C2001_1529480143 &_=1529671539706 #时间戳 r=1529672904066&skey=%40crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a&uin=2821071261&deviceid=e647845000558861&synckey=1_677540346%7C2_677540435%7C3_677540036%7C11_677540181%7C201_1529672373%7C203_1529658601%7C1000_1529658962%7C1001_1529659033%7C2001_1529480143&_=1529671539759

返回值
window.synccheck
={retcode:"0",selector:"2"}  #有消息到来
window.synccheck={retcode:"0",selector:"0"}  #没有消息到来

当有消息到来:我们将从webwxsync中获取到数据

{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
}
,
"AddMsgCount": 1,  #到来的消息数目
"AddMsgList": [{
"MsgId": "3138475736544982077",
"FromUserName": "@@e780498496215d2077ef72216ce629bfd570595c2d1b99192aa32fae78a3e489",  #来自谁的微信
"ToUserName": "@9419ce13e311ec0469803ba1667703ca16fd3e9ec13d5998c52f42dba33f4c3d",  #这是我们的微信
"MsgType": 1,
"Content": "@0e722870282c2e079ec9ddc7304ef501bb99d45465501efac52f8b050498d8a4:<br/>@A~筱嵩灬 10分钟吧",    #发过来的信息,我们未编码
"Status": 3,  #状态
"ImgStatus": 1,
"CreateTime": 1529673345,
"VoiceLength": 0,
"PlayLength": 0,
"FileName": "",
"FileSize": "",
"MediaId": "",
"Url": "",
"AppMsgType": 0,
"StatusNotifyCode": 0,
"StatusNotifyUserName": "",
"RecommendInfo": {
"UserName": "",
"NickName": "",
"QQNum": 0,
"Province": "",
"City": "",
"Content": "",
"Signature": "",
"Alias": "",
"Scene": 0,
"VerifyFlag": 0,
"AttrStatus": 0,
"Sex": 0,
"Ticket": "",
"OpCode": 0
}
,
"ForwardFlag": 0,
"AppInfo": {
"AppID": "",
"Type": 0
}
,
"HasProductId": 0,
"Ticket": "",
"ImgHeight": 0,
"ImgWidth": 0,
"SubMsgType": 0,
"NewMsgId": 3138475736544982077,
"OriContent": "",
"EncryFileName": ""
}
],
"ModContactCount": 0,
"ModContactList": [],
"DelContactCount": 0,
"DelContactList": [],
"ModChatRoomMemberCount": 0,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": 0,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": 0,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": 0,
"Sex": 0,
"PersonalCard": 0,
"Alias": "",
"HeadImgUpdateFlag": 0,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": 0,
"SyncKey": {  #下一次去检测需要的SyncKey,每当我们接受一次真正的消息或者长轮询结束,原来的就失效,需要一个新的SyncKey值
"Count": 9,
"List": [{
"Key": 1,
"Val": 677540346
}
,{
"Key": 2,
"Val": 677540437
}
,{
"Key": 3,
"Val": 677540036
}
,{
"Key": 11,
"Val": 677540181
}
,{
"Key": 201,
"Val": 1529673345
}
,{
"Key": 203,
"Val": 1529658601
}
,{
"Key": 1000,
"Val": 1529658962
}
,{
"Key": 1001,
"Val": 1529659033
}
,{
"Key": 2001,
"Val": 1529480143
}
]
}
,
"SKey": "",
"SyncCheckKey": {
"Count": 9,
"List": [{
"Key": 1,
"Val": 677540346
}
,{
"Key": 2,
"Val": 677540437
}
,{
"Key": 3,
"Val": 677540036
}
,{
"Key": 11,
"Val": 677540181
}
,{
"Key": 201,
"Val": 1529673345
}
,{
"Key": 203,
"Val": 1529658601
}
,{
"Key": 1000,
"Val": 1529658962
}
,{
"Key": 1001,
"Val": 1529659033
}
,{
"Key": 2001,
"Val": 1529480143
}
]
}

}

 后台视图方法获取接收的信息

def get_msg(request):
    ret = {
        'code':400,
        'msg':'no message arrived'
    }
    global SYNCKEY_DICT  #设置为全局
    #1.先去查询是否有消息到达
    req_url = 'https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck'
    sync_key = []
    for item in SYNCKEY_DICT['List']:
        sync_key.append("%s_%s"%(item['Key'],item['Val']))

    reponse = requests.get(
        url=req_url,
        params={
            'r':int(time.time()*1000),
            'skey':TICKET_DICT['skey'],
            'sid':TICKET_DICT['wxsid'],
            'uin':TICKET_DICT['wxuin'],
            'deviceid':'e055319847811019',
            'synckey':'|'.join(sync_key)  #参数重组
        },
        cookies = ALL_COOKIE_DICT
    )

    if 'window.synccheck={retcode:"0",selector:"2"}' in reponse.text:
        ret['code'] = 200
        ret['msg'] = "message arrived"
        #有消息到来,这时我们需要去获取信息,并且更新SYNCKEY_DICT
        req_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync'

        post_dict = {
            'BaseRequest':
                {
                    'DeviceID': "e055319847811019",
                    'Sid': TICKET_DICT['wxsid'],
                    'Skey': TICKET_DICT['skey'],
                    'Uin': TICKET_DICT['wxuin']
                },
            'SyncKey':SYNCKEY_DICT
        }

        reponse2 = requests.post(
            url=req_url,
            params={
                'sid':TICKET_DICT['wxsid'],
                'pass_ticket':TICKET_DICT['pass_ticket'],
                'skey':TICKET_DICT['skey'],
                'lang': 'zh_CN'
            },
            json = post_dict
        )

        reponse2.encoding = "utf-8"
        rep_dict = json.loads(reponse2.text)
        SYNCKEY_DICT = rep_dict.get("SyncKey")
        if rep_dict['AddMsgCount'] != 0:
            ret['code'] = 201
            ret['msg'] = []
            for item in rep_dict['AddMsgList']:
                ret['msg'].append(item)

    return HttpResponse(json.dumps(ret))

前端修改:首先是有get_msg方法去长轮询获取接收消息和检测数据是否接收。然后针对谁发送的,谁就高亮

    <script>
    $(function(){
        bind_event();
        get_msg();
    })

    function bind_event() {
        $(".list-group-item").click(function(){
            $(".list-unstyled").empty();
            $(this).siblings().removeClass("active");
            $(this).addClass("active");
            var NickName = $(this).first().text().trim();
            $(".panel-title").text(NickName);
        })
    }

    function get_msg(){
        $.ajax({
            url:"/get-msg.html",
            type:"GET",
            dataType:"json",
            success:function(callback){
                if (callback.code==201){
                    //这里只去看一个人的信息
                    var info = callback['msg'][0]
                    $(".list-group-item").each(function(){
                        if($(this).attr("for")==info['FromUserName']){
                            $(this).addClass("active")
                            return false;
                        }
                    })
                }
                get_msg();
            }
        })
    }
    </script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/nifty.min.css" rel="stylesheet">
    <link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet">
    <link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet">
    <link href="/static/plugins/pace/pace.min.css" rel="stylesheet">
    <script src="/static/js/jquery-2.2.4.min.js"></script>
    <script>
    $(function(){
        bind_event();
        get_msg();
    })

    function bind_event() {
        $(".list-group-item").click(function(){
            $(".list-unstyled").empty();
            $(this).siblings().removeClass("active");
            $(this).addClass("active");
            var NickName = $(this).first().text().trim();
            $(".panel-title").text(NickName);
        })
    }

    function get_msg(){
        $.ajax({
            url:"/get-msg.html",
            type:"GET",
            dataType:"json",
            success:function(callback){
                if (callback.code==201){
                    //这里只去看一个人的信息
                    var info = callback['msg'][0]
                    $(".list-group-item").each(function(){
                        if($(this).attr("for")==info['FromUserName']){
                            $(this).addClass("active")
                            return false;
                        }
                    })
                }
                get_msg();
            }
        })
    }
    </script>
    <script src="/static/plugins/pace/pace.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <script src="/static/js/nifty.min.js"></script>
    <script src="/static/js/demo/nifty-demo.min.js"></script>
    <script src="/static/plugins/flot-charts/jquery.flot.min.js"></script>
    <script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script>
    <script src="/static/plugins/gauge-js/gauge.min.js"></script>
    <script src="/static/plugins/skycons/skycons.min.js"></script>
    <script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script>
    <script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect  aside-bright mainnav-sm aside-right aside-in">
    <div class="boxed">
        <div id="content-container">
            <div class="row">
                <div class="col-md-8 col-lg-8 col-sm-8">

            <!--Chat widget-->
            <!--===================================================-->
            <div class="panel" style="height: 640px">
                <!--Heading-->
                <div class="panel-heading">
                    <h3 class="panel-title">Chat</h3>
                </div>

                <!--Widget body-->
                <div style="height:510px;padding-top:0px;" class="widget-body">
                    <div class="nano">
                        <div class="nano-content pad-all">
                            <ul class="list-unstyled media-block">
                                <li class="mar-btm">
                                    <div class="media-left">
                                        <img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture">
                                    </div>
                                    <div class="media-body pad-hor">
                                        <div class="speech">
                                            <a href="#" class="media-heading">Aaron Chavez</a>
                                            <p>Hello Lucy, how can I help you today ?</p>
                                            <p class="speech-time">
                                            <i class="demo-pli-clock icon-fw"></i>09:23AM
                                            </p>
                                        </div>
                                    </div>
                                </li>
                                <li class="mar-btm">
                                    <div class="media-right">
                                        <img src="img/profile-photos/8.png" class="img-circle img-sm" alt="Profile Picture">
                                    </div>
                                    <div class="media-body pad-hor speech-right">
                                        <div class="speech">
                                            <a href="#" class="media-heading">Lucy Doe</a>
                                            <p>Hi, I want to buy a new shoes.</p>
                                            <p class="speech-time">
                                                <i class="demo-pli-clock icon-fw"></i> 09:23AM
                                            </p>
                                        </div>
                                    </div>
                                </li>
                            </ul>
                        </div>
                    </div>

                    <!--Widget footer-->
                    <div class="panel-footer" style="height: 90px;">
                        <div class="row">
                            <div class="col-xs-9">
                                <input type="text" placeholder="Enter your text" class="form-control chat-input">
                            </div>
                            <div class="col-xs-3">
                                <button class="btn btn-primary btn-block" onclick="sendMsg(this);" type="submit">Send</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!--===================================================-->
            <!--Chat widget-->

        </div>
                <div class="col-md-4 col-lg-4 col-sm-4">
                    <aside id="aside-container">
                <div id="aside">
                    <div class="nano has-scrollbar">
                        <div class="nano-content" tabindex="0" style="right: -17px;">

                            <!--Nav tabs-->
                            <!--================================-->
                            <ul class="nav nav-tabs nav-justified">
                                <li class="active">
                                    <a href="#demo-asd-tab-1" data-toggle="tab">
                                        <i class="demo-pli-speech-bubble-7"></i>
                                    </a>
                                </li>
                            </ul>
                            <!--================================-->
                            <!--End nav tabs-->

                            <!-- Tabs Content -->
                            <!--================================-->
                            <div class="tab-content">
                                <div class="tab-pane fade in active" id="demo-asd-tab-1">
                                    <p class="pad-hor text-semibold text-main">
                                        <span class="pull-right badge badge-success">{{ contact_info_list.MemberCount }}</span> Friends
                                    </p>

                                    <!--Works-->
                                    <div class="list-group bg-trans">
                                        {% for item in contact_info_list.MemberList %}
                                            <a href="#" for="{{ item.UserName }}" class="list-group-item">
                                                <span class="badge badge-purple badge-icon badge-fw pull-left"></span> {{ item.NickName }}
                                            </a>
                                        {% endfor %}
                                    </div>
                                </div>
                            </div>
                        </div>
                    <div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div>
                </div>
            </aside>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

<script>
    function sendMsg(ths){
        var sel_tag = $(".list-group").find(".active")
        if(sel_tag.length==0){
            return false;
        }
        var msg = $(ths).parents(".panel-footer").find(".chat-input").val();
        var sendMsg={
            'ToUserName':sel_tag.attr("for"),
            'Type':1,
            'Content':msg,
            'csrfmiddlewaretoken':'{{ csrf_token }}'
        }

        $.ajax({
            url:"send-msg.html",
            data:sendMsg,
            type:"POST",
            dataType:"json",
            success:function(callback){
                if (callback.code==200){
                    var dt = new Date()
                    var now_time = dt.toLocaleString();

                    console.log(callback);
                    var li = '<li class="mar-btm"><div class="media-right"><img src="'+callback.headImgUrl+'" class="img-circle img-sm" alt="Profile Picture"></div>';
                    li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">'+callback.username+'</a>';
                    li += '<p>'+msg+'</p>';
                    li += '<p class="speech-time">';
                    li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;
                    li += '</p></div></div></li>';
                    $(ths).parents(".widget-body").find(".list-unstyled").append(li);
                    $(ths).parents(".panel-footer").find(".chat-input").val("");
                }
            }
        })
    }
</script>
contact_info前端代码

window.synccheck={retcode:"0",selector:"2"}
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
}
,
"AddMsgCount": 1,
"AddMsgList": [{
"MsgId": "8087080836430146290",
"FromUserName": "@7ef22458145d7446b96c0c1612549c00991cab433640c86b9c7d5f9d17dc8a43",
"ToUserName": "@fb5a4e79e7d71ec3f11d3351fdec8a1cb5c4df7979041ed9315833e427323198",
"MsgType": 1,
"Content": "",
"Status": 3,
"ImgStatus": 1,
"CreateTime": 1529679858,
"VoiceLength": 0,
"PlayLength": 0,
"FileName": "",
"FileSize": "",
"MediaId": "",
"Url": "",
"AppMsgType": 0,
"StatusNotifyCode": 0,
"StatusNotifyUserName": "",
"RecommendInfo": {
"UserName": "",
"NickName": "",
"QQNum": 0,
"Province": "",
"City": "",
"Content": "",
"Signature": "",
"Alias": "",
"Scene": 0,
"VerifyFlag": 0,
"AttrStatus": 0,
"Sex": 0,
"Ticket": "",
"OpCode": 0
}
,
"ForwardFlag": 0,
"AppInfo": {
"AppID": "",
"Type": 0
}
,
"HasProductId": 0,
"Ticket": "",
"ImgHeight": 0,
"ImgWidth": 0,
"SubMsgType": 0,
"NewMsgId": 8087080836430146290,
"OriContent": "",
"EncryFileName": ""
}
],
"ModContactCount": 0,
"ModContactList": [],
"DelContactCount": 0,
"DelContactList": [],
"ModChatRoomMemberCount": 0,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": 0,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": 0,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": 0,
"Sex": 0,
"PersonalCard": 0,
"Alias": "",
"HeadImgUpdateFlag": 0,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": 0,
"SyncKey": {
"Count": 8,
"List": [{
"Key": 1,
"Val": 677540346
}
,{
"Key": 2,
"Val": 677540469
}
,{
"Key": 3,
"Val": 677540464
}
,{
"Key": 11,
"Val": 677540440
}
,{
"Key": 201,
"Val": 1529679858
}
,{
"Key": 1000,
"Val": 1529658962
}
,{
"Key": 1001,
"Val": 1529659033
}
,{
"Key": 2001,
"Val": 1529480143
}
]
}
,
"SKey": "",
"SyncCheckKey": {
"Count": 8,
"List": [{
"Key": 1,
"Val": 677540346
}
,{
"Key": 2,
"Val": 677540469
}
,{
"Key": 3,
"Val": 677540464
}
,{
"Key": 11,
"Val": 677540440
}
,{
"Key": 201,
"Val": 1529679858
}
,{
"Key": 1000,
"Val": 1529658962
}
,{
"Key": 1001,
"Val": 1529659033
}
,{
"Key": 2001,
"Val": 1529480143
}
]
}

}
后端打印的数据

其实应该更进一步,显示出来我们接受的数据,但是明天考试..不再继续了

八:全部代码

后端:

from django.shortcuts import render,HttpResponse,redirect
from bs4 import BeautifulSoup
import requests
import time,re,json

CTIME = None
QCODE = None
TIP = 1
TICKET_DICT = {}    #保存凭证信息
ALL_COOKIE_DICT = {}    #保存所有COOKIE信息
USER_INIT_DICT = {} #保存用户最近联系信息,和自己的信息
SYNCKEY_DICT = {}    #用于保存获取请求时需要的SyncKey


# Create your views here.
def login(request):
    global CTIME
    global QCODE
    CTIME = int(time.time()*1000)

    data = {
        'appid':'wx782c26e4c19acffb',
        'fun':'new',
        'lang':'zh_CN',
        '_':CTIME
    }

    response = requests.get(
        url="https://login.wx.qq.com/jslogin",
        params=data
    )

    pat_res = re.findall('uuid = "(.*)";',response.text)
    QCODE = pat_res[0]

    return render(request,"login.html",{'qcode':QCODE})

def check_login(request):
    global TIP
    ret = {'code':408,'data':None}
    data = {
        'loginicon':"true",
        'uuid':QCODE,
        'tip':TIP,
        'r':'-577317906',
        '_':int(time.time())
    }
    r1 = requests.get(
        url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
        params=data
    )

    if 'window.code=408' in r1.text:
        print("无人扫描")
        return HttpResponse(json.dumps(ret))
    elif 'window.code=201' in r1.text:
        ret['code'] = 201
        pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[0]
        ret['data'] = pat_ret
        TIP = 0
        return HttpResponse(json.dumps(ret))
    elif 'window.code=200;' in r1.text:
        ret['code'] = 200
        redirect_url = re.findall('window.redirect_uri="(.*)";', r1.text)[0]
        reponse = requests.get(     #获取凭证,这里也开始设置cookie了,所以我们在这里向后需要记录cookie
            url=redirect_url + "&fun=new&version=v2"  # url不够完整,需要我们完善
        )
        ALL_COOKIE_DICT.update(reponse.cookies)

        soup = BeautifulSoup(reponse.text, "lxml")
        info_dict = {}
        for tag in soup.find("error").children:
            info_dict[tag.name] = tag.get_text()

        global TICKET_DICT
        TICKET_DICT.update(info_dict)

        ret['code']=200

        return HttpResponse(json.dumps(ret))


def user(request):
    get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
        'pass_ticket']
    get_user_info_form = {
        'BaseRequest':
            {
                'DeviceID': "e055319847811019",
                'Sid': TICKET_DICT['wxsid'],
                'Skey': TICKET_DICT['skey'],
                'Uin': TICKET_DICT['wxuin']
            }
    }

    reponse2 = requests.post(  # 获取的是用户信息,几个联系人,公众号,自己信息
        url=get_user_info_url,
        json=get_user_info_form,  # 注意这里使用的是json,post不允许传送字典
    )
    ALL_COOKIE_DICT.update(reponse2.cookies)

    reponse2.encoding = "utf-8"

    user_info_dict = json.loads(reponse2.text)  # 获取的是用户信息,几个联系人,公众号,自己信息
    USER_INIT_DICT.update(user_info_dict)
    SYNCKEY_DICT.update(user_info_dict['SyncKey'])

    return render(request, "user.html", {'user_info_dict': user_info_dict})


def contact_list(request):
    get_all_user_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%s&seq=0&skey=%s' % (TICKET_DICT['pass_ticket'],int(time.time()*1000),TICKET_DICT['skey'])

    reponse = requests.get(
        url=get_all_user_url,   #这里需要用到cookie
        cookies=ALL_COOKIE_DICT
    )

    reponse.encoding = "utf-8"
    contact_info_list = json.loads(reponse.text)

    return render(request,"contact_info.html",{'contact_info_list':contact_info_list})

def send_msg(request):
    ret = {
        'code':200,
        'error':'Send Success',
        'data':{}
    }

    recv_data = request.POST

    if not recv_data:
        ret['code']=400
        ret['data']="Send failure"
        return HttpResponse(json.dumps(ret))

    #数据整合
    Send_data={}
    Send_data['BaseRequest'] = {
        'DeviceID': "e055319847811019",
        'Sid': TICKET_DICT['wxsid'],
        'Skey': TICKET_DICT['skey'],
        'Uin': TICKET_DICT['wxuin']
    }

    Send_data['Msg'] = {
        'Type':recv_data.get("Type",1),
        'Content':recv_data.get("Content"),
        'FromUserName':USER_INIT_DICT['User']['UserName'],
        'ToUserName':recv_data.get("ToUserName"),
        'LocalID':int(time.time()*1000),
        'ClientMsgId':int(time.time()*1000),
    }
    Send_data['Scene']=0

    send_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=%s'%TICKET_DICT['pass_ticket']
    # reponse = requests.post(
    #     url=send_url,
    #     json=Send_data, #注意这里如果使用json,会将中文转换为Unicode
    #     cookies=ALL_COOKIE_DICT
    # )
    reponse = requests.post(
        url=send_url,
        #若是有中文,需要加上ensure_ascii=False,若是字符串中含有中文,request传递数据时,将中文转换为字节,无法为我们转换。需要我们提前使用encoding编码,直接传递字节,不让requests为我们转换
        #data可以是字典,字符串,字节,既然对于字典,字符串直接含有中文不正确,直接转字节传送,py3默认是utf-8,所以我们直接传送字节就可以
        data=bytes(json.dumps(Send_data,ensure_ascii=False),encoding="utf-8"), #注意这里如果使用json,会将中文转换为Unicode
        cookies=ALL_COOKIE_DICT
    )

    reponse.encoding = "utf-8"

    ret_status = re.findall('"Ret": (.*),', reponse.text)[0]
    ret_error = re.findall('"ErrMsg": "(.*)"', reponse.text)[0]

    if int(ret_status) != 0:
        ret['code']=401
        ret['data']=ret_error
        return HttpResponse(json.dumps(ret))

    ret['username'] = USER_INIT_DICT['User']['NickName']
    ret['headImgUrl'] = 'https://wx.qq.com'+USER_INIT_DICT['User']['HeadImgUrl']

    return HttpResponse(json.dumps(ret))

def get_msg(request):
    ret = {
        'code':400,
        'msg':'no message arrived'
    }
    global SYNCKEY_DICT
    #1.先去查询是否有消息到达
    req_url = 'https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck'
    sync_key = []
    for item in SYNCKEY_DICT['List']:
        sync_key.append("%s_%s"%(item['Key'],item['Val']))

    reponse = requests.get(
        url=req_url,
        params={
            'r':int(time.time()*1000),
            'skey':TICKET_DICT['skey'],
            'sid':TICKET_DICT['wxsid'],
            'uin':TICKET_DICT['wxuin'],
            'deviceid':'e055319847811019',
            'synckey':'|'.join(sync_key)
        },
        cookies = ALL_COOKIE_DICT
    )

    if 'window.synccheck={retcode:"0",selector:"2"}' in reponse.text:
        ret['code'] = 200
        ret['msg'] = "message arrived"
        #有消息到来,这时我们需要去获取信息,并且更新SYNCKEY_DICT
        req_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync'

        post_dict = {
            'BaseRequest':
                {
                    'DeviceID': "e055319847811019",
                    'Sid': TICKET_DICT['wxsid'],
                    'Skey': TICKET_DICT['skey'],
                    'Uin': TICKET_DICT['wxuin']
                },
            'SyncKey':SYNCKEY_DICT
        }

        reponse2 = requests.post(
            url=req_url,
            params={
                'sid':TICKET_DICT['wxsid'],
                'pass_ticket':TICKET_DICT['pass_ticket'],
                'skey':TICKET_DICT['skey'],
                'lang': 'zh_CN'
            },
            json = post_dict
        )

        reponse2.encoding = "utf-8"
        rep_dict = json.loads(reponse2.text)
        SYNCKEY_DICT = rep_dict.get("SyncKey")
        if rep_dict['AddMsgCount'] != 0:
            ret['code'] = 201
            ret['msg'] = []
            for item in rep_dict['AddMsgList']:
                ret['msg'].append(item)

    return HttpResponse(json.dumps(ret))
views.py

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<img id="qrcode" style=" 340px;height: 340px;" src="https://login.weixin.qq.com/qrcode/{{ qcode }}" alt="">
</body>
</html>
<script src="/static/jquery.js"></script>
<script>
    $(function(){
        checkLogin();
    })

    function checkLogin() {
        $.ajax({
            url:'/check-login.html',
            type:'GET',
            dataType:"json",
            success:function(data){
                console.log(data.code);
                if (data.code==408){
                    checkLogin();
                }
                else if(data.code==201){
                    $("#qrcode").attr('src',data.data)
                    checkLogin();
                }
                else if(data.code==200){
                    location.href='/user.html'
                }
            }
        })
    }
</script>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <h3>最近联系人</h3>
        <ul>
            {% for item in user_info_dict.ContactList %}
                <li>{{ item.NickName }}</li>
            {% endfor %}
        </ul>
        <a href="/contact-list.html">获取更多联系人</a>
    </div>
    <div>
        <h3>微信公众号</h3>
        <div>
            {% for item in user_info_dict.MPSubscribeMsgList %}
                <h4>{{ item.NickName }}</h4>
                <ul>
                    {% for item2 in item.MPArticleList %}
                        <li>
                            <a href="{{ item2.Url }}">
                                {{ item2.Title }}
                            </a>
                        </li>
                    {% endfor %}
                </ul>
            {% endfor %}
        </div>
    </div>
</body>
</html>
user.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/nifty.min.css" rel="stylesheet">
    <link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet">
    <link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet">
    <link href="/static/plugins/pace/pace.min.css" rel="stylesheet">
    <script src="/static/js/jquery-2.2.4.min.js"></script>
    <script>
    $(function(){
        bind_event();
        get_msg();
    })

    function bind_event() {
        $(".list-group-item").click(function(){
            $(".list-unstyled").empty();
            $(this).siblings().removeClass("active");
            $(this).addClass("active");
            var NickName = $(this).first().text().trim();
            $(".panel-title").text(NickName);
        })
    }

    function get_msg(){
        $.ajax({
            url:"/get-msg.html",
            type:"GET",
            dataType:"json",
            success:function(callback){
                if (callback.code==201){
                    //这里只去看一个人的信息
                    var info = callback['msg'][0]
                    $(".list-group-item").each(function(){
                        if($(this).attr("for")==info['FromUserName']){
                            $(this).addClass("active")
                            return false;
                        }
                    })
                }
                get_msg();
            }
        })
    }
    </script>
    <script src="/static/plugins/pace/pace.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <script src="/static/js/nifty.min.js"></script>
    <script src="/static/js/demo/nifty-demo.min.js"></script>
    <script src="/static/plugins/flot-charts/jquery.flot.min.js"></script>
    <script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script>
    <script src="/static/plugins/gauge-js/gauge.min.js"></script>
    <script src="/static/plugins/skycons/skycons.min.js"></script>
    <script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script>
    <script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect  aside-bright mainnav-sm aside-right aside-in">
    <div class="boxed">
        <div id="content-container">
            <div class="row">
                <div class="col-md-8 col-lg-8 col-sm-8">

            <!--Chat widget-->
            <!--===================================================-->
            <div class="panel" style="height: 640px">
                <!--Heading-->
                <div class="panel-heading">
                    <h3 class="panel-title">Chat</h3>
                </div>

                <!--Widget body-->
                <div style="height:510px;padding-top:0px;" class="widget-body">
                    <div class="nano">
                        <div class="nano-content pad-all">
                            <ul class="list-unstyled media-block">
                                <li class="mar-btm">
                                    <div class="media-left">
                                        <img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture">
                                    </div>
                                    <div class="media-body pad-hor">
                                        <div class="speech">
                                            <a href="#" class="media-heading">Aaron Chavez</a>
                                            <p>Hello Lucy, how can I help you today ?</p>
                                            <p class="speech-time">
                                            <i class="demo-pli-clock icon-fw"></i>09:23AM
                                            </p>
                                        </div>
                                    </div>
                                </li>
                                <li class="mar-btm">
                                    <div class="media-right">
                                        <img src="img/profile-photos/8.png" class="img-circle img-sm" alt="Profile Picture">
                                    </div>
                                    <div class="media-body pad-hor speech-right">
                                        <div class="speech">
                                            <a href="#" class="media-heading">Lucy Doe</a>
                                            <p>Hi, I want to buy a new shoes.</p>
                                            <p class="speech-time">
                                                <i class="demo-pli-clock icon-fw"></i> 09:23AM
                                            </p>
                                        </div>
                                    </div>
                                </li>
                            </ul>
                        </div>
                    </div>

                    <!--Widget footer-->
                    <div class="panel-footer" style="height: 90px;">
                        <div class="row">
                            <div class="col-xs-9">
                                <input type="text" placeholder="Enter your text" class="form-control chat-input">
                            </div>
                            <div class="col-xs-3">
                                <button class="btn btn-primary btn-block" onclick="sendMsg(this);" type="submit">Send</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!--===================================================-->
            <!--Chat widget-->

        </div>
                <div class="col-md-4 col-lg-4 col-sm-4">
                    <aside id="aside-container">
                <div id="aside">
                    <div class="nano has-scrollbar">
                        <div class="nano-content" tabindex="0" style="right: -17px;">

                            <!--Nav tabs-->
                            <!--================================-->
                            <ul class="nav nav-tabs nav-justified">
                                <li class="active">
                                    <a href="#demo-asd-tab-1" data-toggle="tab">
                                        <i class="demo-pli-speech-bubble-7"></i>
                                    </a>
                                </li>
                            </ul>
                            <!--================================-->
                            <!--End nav tabs-->

                            <!-- Tabs Content -->
                            <!--================================-->
                            <div class="tab-content">
                                <div class="tab-pane fade in active" id="demo-asd-tab-1">
                                    <p class="pad-hor text-semibold text-main">
                                        <span class="pull-right badge badge-success">{{ contact_info_list.MemberCount }}</span> Friends
                                    </p>

                                    <!--Works-->
                                    <div class="list-group bg-trans">
                                        {% for item in contact_info_list.MemberList %}
                                            <a href="#" for="{{ item.UserName }}" class="list-group-item">
                                                <span class="badge badge-purple badge-icon badge-fw pull-left"></span> {{ item.NickName }}
                                            </a>
                                        {% endfor %}
                                    </div>
                                </div>
                            </div>
                        </div>
                    <div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div>
                </div>
            </aside>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

<script>
    function sendMsg(ths){
        var sel_tag = $(".list-group").find(".active")
        if(sel_tag.length==0){
            return false;
        }
        var msg = $(ths).parents(".panel-footer").find(".chat-input").val();
        var sendMsg={
            'ToUserName':sel_tag.attr("for"),
            'Type':1,
            'Content':msg,
            'csrfmiddlewaretoken':'{{ csrf_token }}'
        }

        $.ajax({
            url:"send-msg.html",
            data:sendMsg,
            type:"POST",
            dataType:"json",
            success:function(callback){
                if (callback.code==200){
                    var dt = new Date()
                    var now_time = dt.toLocaleString();

                    console.log(callback);
                    var li = '<li class="mar-btm"><div class="media-right"><img src="'+callback.headImgUrl+'" class="img-circle img-sm" alt="Profile Picture"></div>';
                    li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">'+callback.username+'</a>';
                    li += '<p>'+msg+'</p>';
                    li += '<p class="speech-time">';
                    li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;
                    li += '</p></div></div></li>';
                    $(ths).parents(".widget-body").find(".list-unstyled").append(li);
                    $(ths).parents(".panel-footer").find(".chat-input").val("");
                }
            }
        })
    }
</script>
contact_info.html

需要:nifty插件可以联系我

原文地址:https://www.cnblogs.com/ssyfj/p/9210725.html