mirror of
https://github.com/xfgryujk/blivechat.git
synced 2025-02-06 01:30:21 +08:00
完成配置功能
This commit is contained in:
parent
920e6fff96
commit
1cbaed4127
@ -5,6 +5,9 @@
|
|||||||
<span id="timestamp">{{time}}</span>
|
<span id="timestamp">{{time}}</span>
|
||||||
<span id="author-name" :type="authorTypeText">{{authorName}}</span>
|
<span id="author-name" :type="authorTypeText">{{authorName}}</span>
|
||||||
<span id="message">{{content}}</span>
|
<span id="message">{{content}}</span>
|
||||||
|
<el-badge :value="repeated" :max="99" v-show="repeated > 1"
|
||||||
|
:style="`--repeated-mark-color: ${repeatedMarkColor}`"
|
||||||
|
></el-badge>
|
||||||
</div>
|
</div>
|
||||||
</yt-live-chat-text-message-renderer>
|
</yt-live-chat-text-message-renderer>
|
||||||
</template>
|
</template>
|
||||||
@ -16,6 +19,8 @@ const AUTHOR_TYPE_TO_TEXT = [
|
|||||||
'moderator', // 房管
|
'moderator', // 房管
|
||||||
'owner' // 主播
|
'owner' // 主播
|
||||||
]
|
]
|
||||||
|
const REPEATED_MARK_COLOR_START = [0x21, 0x96, 0xF3]
|
||||||
|
const REPEATED_MARK_COLOR_END = [0xFF, 0x57, 0x22]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TextMessage',
|
name: 'TextMessage',
|
||||||
@ -24,12 +29,40 @@ export default {
|
|||||||
time: String,
|
time: String,
|
||||||
authorName: String,
|
authorName: String,
|
||||||
authorType: Number,
|
authorType: Number,
|
||||||
content: String
|
content: String,
|
||||||
|
repeated: Number
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
authorTypeText() {
|
authorTypeText() {
|
||||||
return AUTHOR_TYPE_TO_TEXT[this.authorType]
|
return AUTHOR_TYPE_TO_TEXT[this.authorType]
|
||||||
|
},
|
||||||
|
repeatedMarkColor() {
|
||||||
|
let color
|
||||||
|
if (this.repeated <= 2) {
|
||||||
|
color = REPEATED_MARK_COLOR_START
|
||||||
|
} else if (this.repeated >= 10) {
|
||||||
|
color = REPEATED_MARK_COLOR_END
|
||||||
|
} else {
|
||||||
|
color = [0, 0, 0]
|
||||||
|
let t = (this.repeated - 2) / (10 - 2)
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
color[i] = REPEATED_MARK_COLOR_START[i] + (REPEATED_MARK_COLOR_END[i] - REPEATED_MARK_COLOR_START[i]) * t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `rgb(${color.join(', ')})`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
yt-live-chat-text-message-renderer #content .el-badge {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
yt-live-chat-text-message-renderer #content .el-badge * {
|
||||||
|
text-shadow: none !important;
|
||||||
|
font-family: sans-serif !important;
|
||||||
|
background-color: var(--repeated-mark-color) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<template v-for="message in messages">
|
<template v-for="message in messages">
|
||||||
<text-message :key="message.id" v-if="message.type == 0"
|
<text-message :key="message.id" v-if="message.type == 0"
|
||||||
:avatarUrl="message.avatarUrl" :time="message.time" :authorName="message.authorName"
|
:avatarUrl="message.avatarUrl" :time="message.time" :authorName="message.authorName"
|
||||||
:authorType="message.authorType" :content="message.content"
|
:authorType="message.authorType" :content="message.content" :repeated="message.repeated"
|
||||||
></text-message>
|
></text-message>
|
||||||
<legacy-paid-message :key="message.id" v-else-if="message.type == 1"
|
<legacy-paid-message :key="message.id" v-else-if="message.type == 1"
|
||||||
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
||||||
@ -51,11 +51,14 @@ export default {
|
|||||||
PaidMessage
|
PaidMessage
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
let cfg = {...config.DEFAULT_CONFIG}
|
||||||
|
cfg.blockKeywords = cfg.blockKeywords.split('\n').filter(val => val)
|
||||||
|
cfg.blockUsers = cfg.blockUsers.split('\n').filter(val => val)
|
||||||
let styleElement = document.createElement('style')
|
let styleElement = document.createElement('style')
|
||||||
styleElement.innerText = config.DEFAULT_CONFIG.css
|
styleElement.innerText = cfg.css
|
||||||
document.head.appendChild(styleElement)
|
document.head.appendChild(styleElement)
|
||||||
return {
|
return {
|
||||||
config: config.DEFAULT_CONFIG,
|
config: cfg,
|
||||||
styleElement,
|
styleElement,
|
||||||
websocket: null,
|
websocket: null,
|
||||||
messages: [],
|
messages: [],
|
||||||
@ -66,18 +69,46 @@ export default {
|
|||||||
// 开发时使用localhost:80
|
// 开发时使用localhost:80
|
||||||
const url = process.env.NODE_ENV === 'development' ? 'ws://localhost/chat' : `ws://${window.location.host}/chat`
|
const url = process.env.NODE_ENV === 'development' ? 'ws://localhost/chat' : `ws://${window.location.host}/chat`
|
||||||
this.websocket = new WebSocket(url)
|
this.websocket = new WebSocket(url)
|
||||||
this.websocket.onopen = () => this.websocket.send(JSON.stringify({
|
this.websocket.onopen = this.onWsOpen.bind(this)
|
||||||
cmd: COMMAND_JOIN_ROOM,
|
this.websocket.onmessage = this.onWsMessage.bind(this)
|
||||||
data: {
|
|
||||||
roomId: parseInt(this.$route.params.roomId)
|
if (this.$route.query.config_id) {
|
||||||
|
try {
|
||||||
|
let cfg = await config.getRemoteConfig(this.$route.query.config_id)
|
||||||
|
cfg.blockKeywords = cfg.blockKeywords.split('\n').filter(val => val)
|
||||||
|
cfg.blockUsers = cfg.blockUsers.split('\n').filter(val => val)
|
||||||
|
this.styleElement.innerText = cfg.css
|
||||||
|
this.config = cfg
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error('获取配置失败:' + e)
|
||||||
}
|
}
|
||||||
}))
|
}
|
||||||
this.websocket.onmessage = (event) => {
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.head.removeChild(this.styleElement)
|
||||||
|
this.websocket.close()
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
window.scrollTo(0, document.body.scrollHeight)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onWsOpen() {
|
||||||
|
this.websocket.send(JSON.stringify({
|
||||||
|
cmd: COMMAND_JOIN_ROOM,
|
||||||
|
data: {
|
||||||
|
roomId: parseInt(this.$route.params.roomId)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
onWsMessage(event) {
|
||||||
let body = JSON.parse(event.data)
|
let body = JSON.parse(event.data)
|
||||||
let message = null
|
let message = null
|
||||||
let time, price
|
let time, price
|
||||||
switch(body.cmd) {
|
switch(body.cmd) {
|
||||||
case COMMAND_ADD_TEXT:
|
case COMMAND_ADD_TEXT:
|
||||||
|
if (!this.filterTextMessage(body.data) || this.mergeSimilar(body.data.content)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
time = new Date(body.data.timestamp * 1000)
|
time = new Date(body.data.timestamp * 1000)
|
||||||
message = {
|
message = {
|
||||||
id: this.nextId++,
|
id: this.nextId++,
|
||||||
@ -86,7 +117,8 @@ export default {
|
|||||||
time: `${time.getHours()}:${time.getMinutes()}`,
|
time: `${time.getHours()}:${time.getMinutes()}`,
|
||||||
authorName: body.data.authorName,
|
authorName: body.data.authorName,
|
||||||
authorType: body.data.authorType,
|
authorType: body.data.authorType,
|
||||||
content: body.data.content
|
content: body.data.content,
|
||||||
|
repeated: 1
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case COMMAND_ADD_GIFT:
|
case COMMAND_ADD_GIFT:
|
||||||
@ -114,26 +146,49 @@ export default {
|
|||||||
}
|
}
|
||||||
if (message) {
|
if (message) {
|
||||||
this.messages.push(message)
|
this.messages.push(message)
|
||||||
if (this.messages.length > 50)
|
if (this.messages.length > 50) {
|
||||||
this.messages.shift()
|
this.messages.splice(0, this.messages.length - 50)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
filterTextMessage(data) {
|
||||||
if (this.$route.query.config_id) {
|
if (this.config.blockGiftDanmaku && data.isGiftDanmaku) {
|
||||||
try {
|
return false
|
||||||
this.config = await config.getRemoteConfig(this.$route.query.config_id)
|
} else if (this.config.blockLevel > 0 && data.authorLevel < this.config.blockLevel) {
|
||||||
this.styleElement.innerText = this.config.css
|
return false
|
||||||
} catch (e) {
|
} else if (this.config.blockNewbie && data.isNewbie) {
|
||||||
this.$message.error('获取配置失败:' + e)
|
return false
|
||||||
|
} else if (this.config.blockNotMobileVerified && !data.isMobileVerified) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
for (let keyword of this.config.blockKeywords) {
|
||||||
|
if (data.content.indexOf(keyword) !== -1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let user of this.config.blockUsers) {
|
||||||
|
if (data.authorName === user) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
mergeSimilar(content) {
|
||||||
|
if (!this.config.mergeSimilarDanmaku) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (let i = this.messages.length - 1; i >= 0 && i >= this.messages.length - 5; i--) {
|
||||||
|
let message = this.messages[i]
|
||||||
|
if (
|
||||||
|
(message.content.indexOf(content) !== -1 || content.indexOf(message.content) !== -1) // 包含对方
|
||||||
|
&& Math.abs(message.content.length - content.length) < Math.min(message.content.length, content.length) // 长度差比两者长度都小
|
||||||
|
) {
|
||||||
|
message.repeated++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
document.head.removeChild(this.styleElement)
|
|
||||||
this.websocket.close()
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
window.scrollTo(0, document.body.scrollHeight)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
2
main.py
2
main.py
@ -36,7 +36,7 @@ def main():
|
|||||||
(r'/config', views.config.ConfigsHandler),
|
(r'/config', views.config.ConfigsHandler),
|
||||||
(r'/config/(.+)', views.config.ConfigHandler),
|
(r'/config/(.+)', views.config.ConfigHandler),
|
||||||
|
|
||||||
(r'/((css|img|js)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
|
(r'/((css|fonts|img|js)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
|
||||||
(r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
|
(r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
|
||||||
(r'/.*', views.main.MainHandler, {'path': WEB_ROOT})
|
(r'/.*', views.main.MainHandler, {'path': WEB_ROOT})
|
||||||
],
|
],
|
||||||
|
@ -72,7 +72,12 @@ class Room(blivedm.BLiveClient):
|
|||||||
'timestamp': danmaku.timestamp,
|
'timestamp': danmaku.timestamp,
|
||||||
'authorName': danmaku.uname,
|
'authorName': danmaku.uname,
|
||||||
'authorType': author_type,
|
'authorType': author_type,
|
||||||
'content': danmaku.msg
|
'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)
|
||||||
})
|
})
|
||||||
|
|
||||||
async def _on_receive_gift(self, gift: blivedm.GiftMessage):
|
async def _on_receive_gift(self, gift: blivedm.GiftMessage):
|
||||||
@ -80,6 +85,7 @@ class Room(blivedm.BLiveClient):
|
|||||||
return
|
return
|
||||||
self.send_message(Command.ADD_GIFT, {
|
self.send_message(Command.ADD_GIFT, {
|
||||||
'avatarUrl': await get_avatar_url(gift.uid),
|
'avatarUrl': await get_avatar_url(gift.uid),
|
||||||
|
'timestamp': gift.timestamp,
|
||||||
'authorName': gift.uname,
|
'authorName': gift.uname,
|
||||||
'giftName': gift.gift_name,
|
'giftName': gift.gift_name,
|
||||||
'giftNum': gift.num,
|
'giftNum': gift.num,
|
||||||
@ -89,6 +95,7 @@ class Room(blivedm.BLiveClient):
|
|||||||
async def _on_buy_guard(self, message: blivedm.GuardBuyMessage):
|
async def _on_buy_guard(self, message: blivedm.GuardBuyMessage):
|
||||||
self.send_message(Command.ADD_VIP, {
|
self.send_message(Command.ADD_VIP, {
|
||||||
'avatarUrl': await get_avatar_url(message.uid),
|
'avatarUrl': await get_avatar_url(message.uid),
|
||||||
|
'timestamp': message.start_time,
|
||||||
'authorName': message.username
|
'authorName': message.username
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -128,14 +135,24 @@ class RoomManager:
|
|||||||
'timestamp': time.time(),
|
'timestamp': time.time(),
|
||||||
'authorName': 'xfgryujk',
|
'authorName': 'xfgryujk',
|
||||||
'authorType': 0,
|
'authorType': 0,
|
||||||
'content': '我能吞下玻璃而不伤身体'
|
'content': '我能吞下玻璃而不伤身体',
|
||||||
|
'privilegeType': 0,
|
||||||
|
'isGiftDanmaku': False,
|
||||||
|
'authorLevel': 20,
|
||||||
|
'isNewbie': False,
|
||||||
|
'isMobileVerified': True
|
||||||
})
|
})
|
||||||
room.send_message(Command.ADD_TEXT, {
|
room.send_message(Command.ADD_TEXT, {
|
||||||
'avatarUrl': 'https://i0.hdslb.com/bfs/face/29b6be8aa611e70a3d3ac219cdaf5e72b604f2de.jpg@24w_24h.webp',
|
'avatarUrl': 'https://i0.hdslb.com/bfs/face/29b6be8aa611e70a3d3ac219cdaf5e72b604f2de.jpg@24w_24h.webp',
|
||||||
'timestamp': time.time(),
|
'timestamp': time.time(),
|
||||||
'authorName': '主播',
|
'authorName': '主播',
|
||||||
'authorType': 3,
|
'authorType': 3,
|
||||||
'content': "I can eat glass, it doesn't hurt me."
|
'content': "I can eat glass, it doesn't hurt me.",
|
||||||
|
'privilegeType': 0,
|
||||||
|
'isGiftDanmaku': False,
|
||||||
|
'authorLevel': 20,
|
||||||
|
'isNewbie': False,
|
||||||
|
'isMobileVerified': True
|
||||||
})
|
})
|
||||||
room.send_message(Command.ADD_VIP, {
|
room.send_message(Command.ADD_VIP, {
|
||||||
'avatarUrl': 'https://i0.hdslb.com/bfs/face/29b6be8aa611e70a3d3ac219cdaf5e72b604f2de.jpg@24w_24h.webp',
|
'avatarUrl': 'https://i0.hdslb.com/bfs/face/29b6be8aa611e70a3d3ac219cdaf5e72b604f2de.jpg@24w_24h.webp',
|
||||||
@ -195,7 +212,7 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
|||||||
logger.warning('未知的命令: %s data: %s', body['cmd'], body['data'])
|
logger.warning('未知的命令: %s data: %s', body['cmd'], body['data'])
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
logger.info('Websocket断开 %s room: %d', self.request.remote_ip, self.room_id)
|
logger.info('Websocket断开 %s room: %s', self.request.remote_ip, self.room_id)
|
||||||
if self.room_id is not None:
|
if self.room_id is not None:
|
||||||
room_manager.del_client(self.room_id, self)
|
room_manager.del_client(self.room_id, self)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user