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="author-name" :type="authorTypeText">{{authorName}}</span>
|
||||
<span id="message">{{content}}</span>
|
||||
<el-badge :value="repeated" :max="99" v-show="repeated > 1"
|
||||
:style="`--repeated-mark-color: ${repeatedMarkColor}`"
|
||||
></el-badge>
|
||||
</div>
|
||||
</yt-live-chat-text-message-renderer>
|
||||
</template>
|
||||
@ -16,6 +19,8 @@ const AUTHOR_TYPE_TO_TEXT = [
|
||||
'moderator', // 房管
|
||||
'owner' // 主播
|
||||
]
|
||||
const REPEATED_MARK_COLOR_START = [0x21, 0x96, 0xF3]
|
||||
const REPEATED_MARK_COLOR_END = [0xFF, 0x57, 0x22]
|
||||
|
||||
export default {
|
||||
name: 'TextMessage',
|
||||
@ -24,12 +29,40 @@ export default {
|
||||
time: String,
|
||||
authorName: String,
|
||||
authorType: Number,
|
||||
content: String
|
||||
content: String,
|
||||
repeated: Number
|
||||
},
|
||||
computed: {
|
||||
authorTypeText() {
|
||||
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>
|
||||
|
||||
<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">
|
||||
<text-message :key="message.id" v-if="message.type == 0"
|
||||
: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>
|
||||
<legacy-paid-message :key="message.id" v-else-if="message.type == 1"
|
||||
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
||||
@ -51,11 +51,14 @@ export default {
|
||||
PaidMessage
|
||||
},
|
||||
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')
|
||||
styleElement.innerText = config.DEFAULT_CONFIG.css
|
||||
styleElement.innerText = cfg.css
|
||||
document.head.appendChild(styleElement)
|
||||
return {
|
||||
config: config.DEFAULT_CONFIG,
|
||||
config: cfg,
|
||||
styleElement,
|
||||
websocket: null,
|
||||
messages: [],
|
||||
@ -66,18 +69,46 @@ export default {
|
||||
// 开发时使用localhost:80
|
||||
const url = process.env.NODE_ENV === 'development' ? 'ws://localhost/chat' : `ws://${window.location.host}/chat`
|
||||
this.websocket = new WebSocket(url)
|
||||
this.websocket.onopen = () => this.websocket.send(JSON.stringify({
|
||||
this.websocket.onopen = this.onWsOpen.bind(this)
|
||||
this.websocket.onmessage = this.onWsMessage.bind(this)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
},
|
||||
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)
|
||||
}
|
||||
}))
|
||||
this.websocket.onmessage = (event) => {
|
||||
},
|
||||
onWsMessage(event) {
|
||||
let body = JSON.parse(event.data)
|
||||
let message = null
|
||||
let time, price
|
||||
switch(body.cmd) {
|
||||
case COMMAND_ADD_TEXT:
|
||||
if (!this.filterTextMessage(body.data) || this.mergeSimilar(body.data.content)) {
|
||||
break
|
||||
}
|
||||
time = new Date(body.data.timestamp * 1000)
|
||||
message = {
|
||||
id: this.nextId++,
|
||||
@ -86,7 +117,8 @@ export default {
|
||||
time: `${time.getHours()}:${time.getMinutes()}`,
|
||||
authorName: body.data.authorName,
|
||||
authorType: body.data.authorType,
|
||||
content: body.data.content
|
||||
content: body.data.content,
|
||||
repeated: 1
|
||||
}
|
||||
break
|
||||
case COMMAND_ADD_GIFT:
|
||||
@ -114,26 +146,49 @@ export default {
|
||||
}
|
||||
if (message) {
|
||||
this.messages.push(message)
|
||||
if (this.messages.length > 50)
|
||||
this.messages.shift()
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$route.query.config_id) {
|
||||
try {
|
||||
this.config = await config.getRemoteConfig(this.$route.query.config_id)
|
||||
this.styleElement.innerText = this.config.css
|
||||
} catch (e) {
|
||||
this.$message.error('获取配置失败:' + e)
|
||||
if (this.messages.length > 50) {
|
||||
this.messages.splice(0, this.messages.length - 50)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.head.removeChild(this.styleElement)
|
||||
this.websocket.close()
|
||||
filterTextMessage(data) {
|
||||
if (this.config.blockGiftDanmaku && data.isGiftDanmaku) {
|
||||
return false
|
||||
} else if (this.config.blockLevel > 0 && data.authorLevel < this.config.blockLevel) {
|
||||
return false
|
||||
} else if (this.config.blockNewbie && data.isNewbie) {
|
||||
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
|
||||
},
|
||||
updated() {
|
||||
window.scrollTo(0, document.body.scrollHeight)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
2
main.py
2
main.py
@ -36,7 +36,7 @@ def main():
|
||||
(r'/config', views.config.ConfigsHandler),
|
||||
(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'/.*', views.main.MainHandler, {'path': WEB_ROOT})
|
||||
],
|
||||
|
@ -72,7 +72,12 @@ class Room(blivedm.BLiveClient):
|
||||
'timestamp': danmaku.timestamp,
|
||||
'authorName': danmaku.uname,
|
||||
'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):
|
||||
@ -80,6 +85,7 @@ class Room(blivedm.BLiveClient):
|
||||
return
|
||||
self.send_message(Command.ADD_GIFT, {
|
||||
'avatarUrl': await get_avatar_url(gift.uid),
|
||||
'timestamp': gift.timestamp,
|
||||
'authorName': gift.uname,
|
||||
'giftName': gift.gift_name,
|
||||
'giftNum': gift.num,
|
||||
@ -89,6 +95,7 @@ class Room(blivedm.BLiveClient):
|
||||
async def _on_buy_guard(self, message: blivedm.GuardBuyMessage):
|
||||
self.send_message(Command.ADD_VIP, {
|
||||
'avatarUrl': await get_avatar_url(message.uid),
|
||||
'timestamp': message.start_time,
|
||||
'authorName': message.username
|
||||
})
|
||||
|
||||
@ -128,14 +135,24 @@ class RoomManager:
|
||||
'timestamp': time.time(),
|
||||
'authorName': 'xfgryujk',
|
||||
'authorType': 0,
|
||||
'content': '我能吞下玻璃而不伤身体'
|
||||
'content': '我能吞下玻璃而不伤身体',
|
||||
'privilegeType': 0,
|
||||
'isGiftDanmaku': False,
|
||||
'authorLevel': 20,
|
||||
'isNewbie': False,
|
||||
'isMobileVerified': True
|
||||
})
|
||||
room.send_message(Command.ADD_TEXT, {
|
||||
'avatarUrl': 'https://i0.hdslb.com/bfs/face/29b6be8aa611e70a3d3ac219cdaf5e72b604f2de.jpg@24w_24h.webp',
|
||||
'timestamp': time.time(),
|
||||
'authorName': '主播',
|
||||
'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, {
|
||||
'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'])
|
||||
|
||||
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:
|
||||
room_manager.del_client(self.room_id, self)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user