mirror of
https://github.com/xfgryujk/blivechat.git
synced 2025-01-15 23:00:32 +08:00
websocket消息优化
This commit is contained in:
parent
49cdc56cff
commit
1c5df01fbc
132
api/chat.py
132
api/chat.py
@ -4,6 +4,7 @@ import asyncio
|
||||
import enum
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
from typing import *
|
||||
|
||||
@ -58,7 +59,7 @@ class Room(blivedm.BLiveClient):
|
||||
def __parse_gift(self, command):
|
||||
data = command['data']
|
||||
return self._on_receive_gift(blivedm.GiftMessage(
|
||||
data['giftName'], data['num'], data['uname'], data['face'], None,
|
||||
None, None, data['uname'], data['face'], None,
|
||||
data['uid'], data['timestamp'], None, None,
|
||||
None, None, None, data['coin_type'], data['total_coin']
|
||||
))
|
||||
@ -121,19 +122,31 @@ class Room(blivedm.BLiveClient):
|
||||
author_type = 1 # 舰队
|
||||
else:
|
||||
author_type = 0
|
||||
self.send_message(Command.ADD_TEXT, {
|
||||
'avatarUrl': await models.avatar.get_avatar_url(danmaku.uid),
|
||||
'timestamp': danmaku.timestamp,
|
||||
'authorName': danmaku.uname,
|
||||
'authorType': author_type,
|
||||
'content': danmaku.msg,
|
||||
'privilegeType': danmaku.privilege_type,
|
||||
'isGiftDanmaku': bool(danmaku.msg_type),
|
||||
'authorLevel': danmaku.user_level,
|
||||
'isNewbie': danmaku.urank < 10000,
|
||||
'isMobileVerified': bool(danmaku.mobile_verify),
|
||||
'medalLevel': 0 if danmaku.room_id != self.room_id else danmaku.medal_level
|
||||
})
|
||||
# 为了节省带宽用list而不是dict
|
||||
self.send_message(Command.ADD_TEXT, [
|
||||
# 0: avatarUrl
|
||||
await models.avatar.get_avatar_url(danmaku.uid),
|
||||
# 1: timestamp
|
||||
danmaku.timestamp,
|
||||
# 2: authorName
|
||||
danmaku.uname,
|
||||
# 3: authorType
|
||||
author_type,
|
||||
# 4: content
|
||||
danmaku.msg,
|
||||
# 5: privilegeType
|
||||
danmaku.privilege_type,
|
||||
# 6: isGiftDanmaku
|
||||
1 if danmaku.msg_type else 0,
|
||||
# 7: authorLevel
|
||||
danmaku.user_level,
|
||||
# 8: isNewbie
|
||||
1 if danmaku.urank < 10000 else 0,
|
||||
# 9: isMobileVerified
|
||||
1 if danmaku.mobile_verify else 0,
|
||||
# 10: medalLevel
|
||||
0 if danmaku.room_id != self.room_id else danmaku.medal_level
|
||||
])
|
||||
|
||||
async def _on_receive_gift(self, gift: blivedm.GiftMessage):
|
||||
avatar_url = models.avatar.process_avatar_url(gift.face)
|
||||
@ -144,8 +157,6 @@ class Room(blivedm.BLiveClient):
|
||||
'avatarUrl': avatar_url,
|
||||
'timestamp': gift.timestamp,
|
||||
'authorName': gift.uname,
|
||||
'giftName': gift.gift_name,
|
||||
'giftNum': gift.num,
|
||||
'totalCoin': gift.total_coin
|
||||
})
|
||||
|
||||
@ -191,7 +202,7 @@ class RoomManager:
|
||||
logger.info('%d clients in room %s', len(room.clients), room_id)
|
||||
|
||||
if client.application.settings['debug']:
|
||||
client.send_test_message()
|
||||
await client.send_test_message()
|
||||
|
||||
def del_client(self, room_id, client: 'ChatHandler'):
|
||||
if room_id not in self._rooms:
|
||||
@ -230,29 +241,49 @@ class RoomManager:
|
||||
class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._close_on_timeout_future = None
|
||||
self.room_id = None
|
||||
|
||||
def open(self):
|
||||
logger.info('Websocket connected %s', self.request.remote_ip)
|
||||
self._close_on_timeout_future = asyncio.ensure_future(self._close_on_timeout())
|
||||
|
||||
async def _close_on_timeout(self):
|
||||
try:
|
||||
# 超过一定时间还没加入房间则断开
|
||||
await asyncio.sleep(10)
|
||||
logger.warning('Client %s joining room timed out', self.request.remote_ip)
|
||||
self.close()
|
||||
except (asyncio.CancelledError, tornado.websocket.WebSocketClosedError):
|
||||
pass
|
||||
|
||||
def on_message(self, message):
|
||||
try:
|
||||
body = json.loads(message)
|
||||
cmd = body['cmd']
|
||||
if cmd == Command.HEARTBEAT:
|
||||
pass
|
||||
return
|
||||
elif cmd == Command.JOIN_ROOM:
|
||||
if self.room_id is not None:
|
||||
if self.has_joined_room:
|
||||
return
|
||||
self.room_id = int(body['data']['roomId'])
|
||||
logger.info('Client %s is joining room %d', self.request.remote_ip, self.room_id)
|
||||
|
||||
asyncio.ensure_future(room_manager.add_client(self.room_id, self))
|
||||
self._close_on_timeout_future.cancel()
|
||||
self._close_on_timeout_future = None
|
||||
else:
|
||||
logger.warning('Unknown cmd: %s body: %s', cmd, body)
|
||||
logger.warning('Unknown cmd, client: %s, cmd: %d, body: %s', self.request.remote_ip, cmd, body)
|
||||
except:
|
||||
logger.exception('on_message error, client: %s, message: %s', self.request.remote_ip, message)
|
||||
|
||||
def on_close(self):
|
||||
logger.info('Websocket disconnected %s room: %s', self.request.remote_ip, str(self.room_id))
|
||||
if self.room_id is not None:
|
||||
if self.has_joined_room:
|
||||
room_manager.del_client(self.room_id, self)
|
||||
if self._close_on_timeout_future is not None:
|
||||
self._close_on_timeout_future.cancel()
|
||||
self._close_on_timeout_future = None
|
||||
|
||||
# 跨域测试用
|
||||
def check_origin(self, origin):
|
||||
@ -261,52 +292,67 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
return super().check_origin(origin)
|
||||
|
||||
# 测试用
|
||||
def send_test_message(self):
|
||||
async def send_test_message(self):
|
||||
base_data = {
|
||||
'avatarUrl': '//i0.hdslb.com/bfs/face/29b6be8aa611e70a3d3ac219cdaf5e72b604f2de.jpg@48w_48h',
|
||||
'avatarUrl': await models.avatar.get_avatar_url(300474),
|
||||
'timestamp': time.time(),
|
||||
'authorName': 'xfgryujk',
|
||||
}
|
||||
text_data = {
|
||||
**base_data,
|
||||
'authorType': 0,
|
||||
'content': '我能吞下玻璃而不伤身体',
|
||||
'privilegeType': 0,
|
||||
'isGiftDanmaku': False,
|
||||
'authorLevel': 20,
|
||||
'isNewbie': False,
|
||||
'isMobileVerified': True
|
||||
}
|
||||
member_data = base_data
|
||||
text_data = [
|
||||
# 0: avatarUrl
|
||||
base_data['avatarUrl'],
|
||||
# 1: timestamp
|
||||
base_data['timestamp'],
|
||||
# 2: authorName
|
||||
base_data['authorName'],
|
||||
# 3: authorType
|
||||
0,
|
||||
# 4: content
|
||||
'我能吞下玻璃而不伤身体',
|
||||
# 5: privilegeType
|
||||
0,
|
||||
# 6: isGiftDanmaku
|
||||
0,
|
||||
# 7: authorLevel
|
||||
20,
|
||||
# 8: isNewbie
|
||||
0,
|
||||
# 9: isMobileVerified
|
||||
1,
|
||||
# 10: medalLevel
|
||||
0
|
||||
]
|
||||
member_data = base_data.copy()
|
||||
gift_data = {
|
||||
**base_data,
|
||||
'giftName': '摩天大楼',
|
||||
'giftNum': 1,
|
||||
'totalCoin': 450000
|
||||
}
|
||||
sc_data = {
|
||||
**base_data,
|
||||
'price': 30,
|
||||
'content': 'The quick brown fox jumps over the lazy dog',
|
||||
'id': 1
|
||||
'id': random.randint(1, 65535)
|
||||
}
|
||||
self.send_message(Command.ADD_TEXT, text_data)
|
||||
text_data['authorName'] = '主播'
|
||||
text_data['authorType'] = 3
|
||||
text_data['content'] = "I can eat glass, it doesn't hurt me."
|
||||
text_data[2] = '主播'
|
||||
text_data[3] = 3
|
||||
text_data[4] = "I can eat glass, it doesn't hurt me."
|
||||
self.send_message(Command.ADD_TEXT, text_data)
|
||||
self.send_message(Command.ADD_MEMBER, member_data)
|
||||
self.send_message(Command.ADD_SUPER_CHAT, sc_data)
|
||||
sc_data['price'] = 100
|
||||
sc_data['content'] = '敏捷的棕色狐狸跳过了懒狗'
|
||||
sc_data['id'] = 2
|
||||
sc_data['id'] = random.randint(1, 65535)
|
||||
self.send_message(Command.ADD_SUPER_CHAT, sc_data)
|
||||
# self.send_message(Command.DEL_SUPER_CHAT, {'ids': [1, 2]})
|
||||
# self.send_message(Command.DEL_SUPER_CHAT, {'ids': [sc_data['id']]})
|
||||
self.send_message(Command.ADD_GIFT, gift_data)
|
||||
gift_data['giftName'] = '小电视飞船'
|
||||
gift_data['totalCoin'] = 1245000
|
||||
self.send_message(Command.ADD_GIFT, gift_data)
|
||||
|
||||
@property
|
||||
def has_joined_room(self):
|
||||
return self.room_id is not None
|
||||
|
||||
def send_message(self, cmd, data):
|
||||
body = json.dumps({'cmd': cmd, 'data': data})
|
||||
try:
|
||||
|
@ -112,9 +112,21 @@ export default {
|
||||
onWsMessage(event) {
|
||||
let {cmd, data} = JSON.parse(event.data)
|
||||
let message = null
|
||||
let time = data.timestamp ? new Date(data.timestamp * 1000) : new Date()
|
||||
switch (cmd) {
|
||||
case COMMAND_ADD_TEXT:
|
||||
data = {
|
||||
avatarUrl: data[0],
|
||||
timestamp: data[1],
|
||||
authorName: data[2],
|
||||
authorType: data[3],
|
||||
content: data[4],
|
||||
privilegeType: data[5],
|
||||
isGiftDanmaku: !!data[6],
|
||||
authorLevel: data[7],
|
||||
isNewbie: !!data[8],
|
||||
isMobileVerified: !!data[9],
|
||||
medalLevel: data[10]
|
||||
}
|
||||
if (!this.config.showDanmaku || !this.filterTextMessage(data) || this.mergeSimilarText(data.content)) {
|
||||
break
|
||||
}
|
||||
@ -122,7 +134,7 @@ export default {
|
||||
id: `text_${this.nextId++}`,
|
||||
type: constants.MESSAGE_TYPE_TEXT,
|
||||
avatarUrl: data.avatarUrl,
|
||||
time: time,
|
||||
time: new Date(data.timestamp * 1000),
|
||||
authorName: data.authorName,
|
||||
authorType: data.authorType,
|
||||
content: data.content,
|
||||
@ -147,7 +159,7 @@ export default {
|
||||
avatarUrl: data.avatarUrl,
|
||||
authorName: data.authorName,
|
||||
price: price,
|
||||
time: time,
|
||||
time: new Date(data.timestamp * 1000),
|
||||
content: '' // 有了SC,礼物不需要内容了
|
||||
}
|
||||
break
|
||||
@ -160,7 +172,7 @@ export default {
|
||||
id: `member_${this.nextId++}`,
|
||||
type: constants.MESSAGE_TYPE_MEMBER,
|
||||
avatarUrl: data.avatarUrl,
|
||||
time: time,
|
||||
time: new Date(data.timestamp * 1000),
|
||||
authorName: data.authorName,
|
||||
title: 'NEW MEMBER!',
|
||||
content: `Welcome ${data.authorName}!`
|
||||
@ -179,7 +191,7 @@ export default {
|
||||
avatarUrl: data.avatarUrl,
|
||||
authorName: data.authorName,
|
||||
price: data.price,
|
||||
time: time,
|
||||
time: new Date(data.timestamp * 1000),
|
||||
content: data.content.trim()
|
||||
}
|
||||
break
|
||||
|
Loading…
Reference in New Issue
Block a user