mirror of
https://github.com/wbt5/real-url.git
synced 2025-08-01 14:48:01 +08:00
219 lines
7.3 KiB
Python
219 lines
7.3 KiB
Python
from . import huajiao_pb2 as pb
|
||
import struct
|
||
import hashlib
|
||
import random
|
||
import string
|
||
import json
|
||
import time
|
||
|
||
|
||
class HuaJiao:
|
||
|
||
heartbeat = b'\x00\x00\x00\x00'
|
||
ws_url = 'wss://bridge.huajiao.com'
|
||
|
||
def __init__(self, rid=None):
|
||
self.sn = ''
|
||
self.tt = str(int(time.time() * 1000))
|
||
self.roomId = rid
|
||
self.flag = 'qh'
|
||
self.protocolVersion = 1
|
||
self.clientVersion = 101
|
||
self.appId = 2080
|
||
self.sender = self.password = '999' + self.tt + self.random_(6, 'n')
|
||
self.defaultKey = '3f190210cb1cf32a2378ee57900acf78'
|
||
|
||
def init_p(self):
|
||
p = pb.Message()
|
||
p.sn = int(self.random_(10, 'n'))
|
||
p.sender = self.sender
|
||
p.sender_type = 'jid'
|
||
return p
|
||
|
||
@staticmethod
|
||
def random_(num, var):
|
||
seq = ''
|
||
if var == 's':
|
||
seq = string.ascii_letters + string.digits
|
||
if var == 'n':
|
||
seq = string.digits
|
||
result = ''.join([random.choice(seq) for i in range(num)])
|
||
return result
|
||
|
||
@staticmethod
|
||
def md5(data):
|
||
return hashlib.md5(data.encode('utf-8')).hexdigest()
|
||
|
||
@staticmethod
|
||
def rc4(data, key):
|
||
a = [i for i in range(256)]
|
||
l = i = 0
|
||
while i < 256:
|
||
l = (l + a[i] + ord(key[i % len(key)])) % 256
|
||
a[i], a[l] = a[l], a[i]
|
||
i += 1
|
||
i = l = n = 0
|
||
s = len(data)
|
||
f = []
|
||
while n < s:
|
||
i = (i + 1) % 256
|
||
l = (l + a[i]) % 256
|
||
a[i], a[l] = a[l], a[i]
|
||
f.append(data[n] ^ a[(a[i] + a[l]) % 256])
|
||
n += 1
|
||
return bytes(f)
|
||
|
||
def sendHandshakePack(self):
|
||
HandshakePack = struct.pack('!2sbbhih', self.flag.encode(), self.protocolVersion << 4, self.clientVersion,
|
||
self.appId, 0, 0)
|
||
p = self.init_p()
|
||
self.sn = p.sn
|
||
p.msgid = 100009
|
||
p.req.init_login_req.client_ram = self.random_(10, 's')
|
||
p.req.init_login_req.sig = ''
|
||
data = p.SerializeToString()
|
||
a = self.rc4(data, self.defaultKey)
|
||
HandshakePack += struct.pack('!i', len(HandshakePack + a) + 4) + a
|
||
return HandshakePack
|
||
|
||
def processHandShakePack(self, message):
|
||
o, = struct.unpack('!2s', message[:2])
|
||
if o.decode() != self.flag:
|
||
raise Exception('processHandShakePack 服务器响应标识(flag)有误')
|
||
s = self.rc4(message[6:], self.defaultKey)
|
||
p = self.init_p()
|
||
try:
|
||
p.ParseFromString(s)
|
||
except:
|
||
raise Exception('processHandShakePack 解析消息体异常')
|
||
if p.msgid != 200009:
|
||
raise Exception('processHandShakePack 响应msgid异常')
|
||
if p.sn != self.sn:
|
||
raise Exception('processHandShakePack sn验证失败')
|
||
return p.resp
|
||
|
||
def sendLoginPack(self, message):
|
||
e = self.processHandShakePack(message)
|
||
u = e.init_login_resp.server_ram
|
||
secret_ram = self.rc4((u + self.random_(8, 's')).encode(), self.password)
|
||
verf_code = self.md5(self.sender + '360tantan@1408$')[24:]
|
||
|
||
p = self.init_p()
|
||
self.sn = p.sn
|
||
p.msgid = 100001
|
||
|
||
p.req.login.app_id = 2080
|
||
p.req.login.mobile_type = 'ios'
|
||
p.req.login.net_type = 4
|
||
p.req.login.not_encrypt = True
|
||
p.req.login.platform = 'h5'
|
||
p.req.login.server_ram = u
|
||
p.req.login.secret_ram = secret_ram
|
||
p.req.login.verf_code = verf_code
|
||
|
||
a = p.SerializeToString()
|
||
l = self.rc4(a, self.defaultKey)
|
||
LoginPack = struct.pack('!i', 4 + len(l)) + l
|
||
return LoginPack
|
||
|
||
def processLoginPack(self, message):
|
||
p = self.init_p()
|
||
try:
|
||
p.ParseFromString(self.rc4(message[4:], self.password))
|
||
except:
|
||
try:
|
||
p.ParseFromString(self.rc4(message[4:], self.defaultKey))
|
||
except:
|
||
raise Exception('processLoginPack 解析消息体异常')
|
||
if p.msgid != 200001:
|
||
raise Exception('processLoginPack 响应msgid异常')
|
||
if p.sn != self.sn:
|
||
raise Exception('processLoginPack sn验证失败')
|
||
|
||
return p
|
||
|
||
def sendJoinChatroomPack(self, message):
|
||
p = self.processLoginPack(message)
|
||
o = self.roomId.encode()
|
||
# crm : ChatroomRequestMessage
|
||
crm = pb.ChatRoomPacket()
|
||
crm.client_sn = p.sn
|
||
crm.roomid = o
|
||
crm.appid = self.appId
|
||
crm.uuid = self.md5(self.random_(10, 's') + '0000000001' + str(int(time.time() * 1000)))
|
||
crm.to_server_data.payloadtype = 102
|
||
crm.to_server_data.applyjoinchatroomreq.roomid = o
|
||
crm.to_server_data.applyjoinchatroomreq.room.roomid = o
|
||
crm.to_server_data.applyjoinchatroomreq.userid_type = 0
|
||
|
||
p = self.init_p()
|
||
self.sn = p.sn
|
||
p.msgid = 100011
|
||
p.req.service_req.service_id = 10000006
|
||
p.req.service_req.request = crm.SerializeToString()
|
||
|
||
u = p.SerializeToString()
|
||
JoinChatroomPack = struct.pack('!i', 4 + len(u)) + u
|
||
|
||
return JoinChatroomPack
|
||
|
||
def processMessagePack(self, message):
|
||
i, = struct.unpack_from('!i', message, 0)
|
||
if len(message) == 4 and i == 0: # HeartbeatPack
|
||
return None
|
||
|
||
p = self.init_p()
|
||
p.ParseFromString(message[4:])
|
||
o = p.msgid
|
||
|
||
if o == 200011:
|
||
return self.processService_RespMessage(p)
|
||
elif o == 300000:
|
||
return self.processNewMessageNotifyMessage(p)
|
||
else:
|
||
return None
|
||
|
||
def processService_RespMessage(self, p):
|
||
if p.sn != self.sn:
|
||
raise Exception('processService_RespMessage sn验证失败')
|
||
crp = pb.ChatRoomPacket()
|
||
crp.ParseFromString(p.resp.service_resp.response)
|
||
n = crp.to_user_data
|
||
r = i = ''
|
||
if n.payloadtype == 102 or n.applyjoinchatroomresp:
|
||
if n.result == 0:
|
||
r = n.applyjoinchatroomresp.room.properties[1].value
|
||
i = n.applyjoinchatroomresp.room.partnerdata
|
||
r = r.decode('utf-8')
|
||
i = i.decode('utf-8')
|
||
return r, i
|
||
|
||
def processNewMessageNotifyMessage(self, p):
|
||
crp = pb.ChatRoomPacket()
|
||
crp.ParseFromString(p.notify.newinfo_ntf.info_content)
|
||
r = crp.to_user_data
|
||
s = i = ''
|
||
if r.result == 0:
|
||
if r.payloadtype == 1000 and r.newmsgnotify:
|
||
s = r.newmsgnotify.memcount
|
||
i = r.newmsgnotify.msgcontent
|
||
elif r.payloadtype == 1001 and r.memberjoinnotify:
|
||
s = r.memberjoinnotify.room.properties[1].value
|
||
i = r.memberjoinnotify.room.members[0].userdata
|
||
elif r.payloadtype == 1002 and r.memberquitnotify:
|
||
s = r.memberquitnotify.room.properties[0].value
|
||
i = r.memberquitnotify.room.members[0].userdata
|
||
i = i.decode('utf-8')
|
||
i = json.loads(i)
|
||
return s, i
|
||
|
||
def decode_msg(self, message):
|
||
msgs = []
|
||
memcountmsg, msgcontent = self.processMessagePack(message)
|
||
if msgcontent.get('type') == 9:
|
||
name = msgcontent['extends']['nickname']
|
||
content = msgcontent['text']
|
||
msg = {'name': name, 'content': content, 'msg_type': 'danmaku'}
|
||
msgs.append(msg)
|
||
return msgs
|