一:轮询,长轮询,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+GQ+NEaghUBCxbF3xPJSzqXUGTO6BYh3TBEGlw8Wa7qETkA9EEAUudYU</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/1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ</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=
"width: 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%2BoP10LCLTdmjnFgEQOCK9n7401krRfKnc0xMbNweJ
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" οnclick=
"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=@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a&uin=2821071261&deviceid=e539832593408529&synckey=1_677540346|2_677540434|3_677540036|11_677540181|201_1529672366|203_1529658601|1000_1529658962|1001_1529659033|2001_1529480143&_=1529671539738
https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?
r=
1529671730362 #时间戳
&skey=@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=
NOWucA2Et3xw0l8a
&uin=
2821071261 #和其他数据一起放在TICKET_DICT中
&deviceid=
e065841963151848 #设备id
&synckey=
1_677540346 1 677540346 #和上一个请求url中接收的数据一致
| |
2_677540423 2 677540423
| |
3_677540036
%
7C
11_677540181|201_1529671726|203_1529658601|1000_1529658962|1001_1529659033%
7C2001_1529480143
&_=
1529671539706 #时间戳
r=
1529672904066&skey=@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a&uin=
2821071261&deviceid=e647845000558861&synckey=1_677540346|2_677540435|3_677540036|11_677540181|201_1529672373|203_1529658601|1000_1529658962|1001_1529659033|2001_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" οnclick=
"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=
"width: 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" οnclick=
"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
相关资源:Python-weixinlogin微信网页授权实现扫码登录