mirror of
https://github.com/xfgryujk/blivechat.git
synced 2026-08-23 11:53:28 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63644f5499 | ||
|
|
8a57304790 | ||
|
|
d7df071595 | ||
|
|
6e8b04ef4f | ||
|
|
b73a4e3748 | ||
|
|
102746d0b0 | ||
|
|
2d237af6fd | ||
|
|
4030ff43cd |
52
api/chat.py
52
api/chat.py
@@ -63,7 +63,7 @@ class Room(blivedm.BLiveClient):
|
||||
def __parse_gift(self, command):
|
||||
data = command['data']
|
||||
return self._on_receive_gift(blivedm.GiftMessage(
|
||||
None, None, data['uname'], data['face'], None,
|
||||
data['giftName'], data['num'], data['uname'], data['face'], None,
|
||||
data['uid'], data['timestamp'], None, None,
|
||||
None, None, None, data['coin_type'], data['total_coin']
|
||||
))
|
||||
@@ -113,7 +113,7 @@ class Room(blivedm.BLiveClient):
|
||||
try:
|
||||
client.write_message(body)
|
||||
except tornado.websocket.WebSocketClosedError:
|
||||
pass
|
||||
room_manager.del_client(self.room_id, client)
|
||||
|
||||
def send_message_if(self, can_send_func: Callable[['ChatHandler'], bool], cmd, data):
|
||||
body = json.dumps({'cmd': cmd, 'data': data})
|
||||
@@ -121,7 +121,7 @@ class Room(blivedm.BLiveClient):
|
||||
try:
|
||||
client.write_message(body)
|
||||
except tornado.websocket.WebSocketClosedError:
|
||||
pass
|
||||
room_manager.del_client(self.room_id, client)
|
||||
|
||||
async def _on_receive_danmaku(self, danmaku: blivedm.DanmakuMessage):
|
||||
asyncio.ensure_future(self.__on_receive_danmaku(danmaku))
|
||||
@@ -192,7 +192,9 @@ class Room(blivedm.BLiveClient):
|
||||
'avatarUrl': avatar_url,
|
||||
'timestamp': gift.timestamp,
|
||||
'authorName': gift.uname,
|
||||
'totalCoin': gift.total_coin
|
||||
'totalCoin': gift.total_coin,
|
||||
'giftName': gift.gift_name,
|
||||
'num': gift.num
|
||||
})
|
||||
|
||||
async def _on_buy_guard(self, message: blivedm.GuardBuyMessage):
|
||||
@@ -273,7 +275,10 @@ class RoomManager:
|
||||
if not await self._add_room(room_id):
|
||||
client.close()
|
||||
return
|
||||
room = self._rooms[room_id]
|
||||
room = self._rooms.get(room_id, None)
|
||||
if room is None:
|
||||
return
|
||||
|
||||
room.clients.append(client)
|
||||
logger.info('%d clients in room %s', len(room.clients), room_id)
|
||||
if client.auto_translate:
|
||||
@@ -283,13 +288,20 @@ class RoomManager:
|
||||
await client.send_test_message()
|
||||
|
||||
def del_client(self, room_id, client: 'ChatHandler'):
|
||||
if room_id not in self._rooms:
|
||||
room = self._rooms.get(room_id, None)
|
||||
if room is None:
|
||||
return
|
||||
room = self._rooms[room_id]
|
||||
room.clients.remove(client)
|
||||
logger.info('%d clients in room %s', len(room.clients), room_id)
|
||||
if client.auto_translate:
|
||||
room.auto_translate_count -= 1
|
||||
|
||||
try:
|
||||
room.clients.remove(client)
|
||||
except ValueError:
|
||||
# _add_room未完成,没有执行到room.clients.append
|
||||
pass
|
||||
else:
|
||||
logger.info('%d clients in room %s', len(room.clients), room_id)
|
||||
if client.auto_translate:
|
||||
room.auto_translate_count = max(0, room.auto_translate_count - 1)
|
||||
|
||||
if not room.clients:
|
||||
self._del_room(room_id)
|
||||
|
||||
@@ -297,24 +309,25 @@ class RoomManager:
|
||||
if room_id in self._rooms:
|
||||
return True
|
||||
logger.info('Creating room %d', room_id)
|
||||
room = Room(room_id)
|
||||
self._rooms[room_id] = room
|
||||
self._rooms[room_id] = room = Room(room_id)
|
||||
if await room.init_room():
|
||||
room.start()
|
||||
logger.info('%d rooms', len(self._rooms))
|
||||
return True
|
||||
else:
|
||||
self._del_room(room_id)
|
||||
return False
|
||||
|
||||
def _del_room(self, room_id):
|
||||
if room_id not in self._rooms:
|
||||
room = self._rooms.get(room_id, None)
|
||||
if room is None:
|
||||
return
|
||||
logger.info('Removing room %d', room_id)
|
||||
room = self._rooms[room_id]
|
||||
for client in room.clients:
|
||||
client.close()
|
||||
room.stop_and_close()
|
||||
del self._rooms[room_id]
|
||||
self._rooms.pop(room_id, None)
|
||||
logger.info('%d rooms', len(self._rooms))
|
||||
|
||||
|
||||
# noinspection PyAbstractClass
|
||||
@@ -419,7 +432,9 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
gift_data = {
|
||||
**base_data,
|
||||
'id': uuid.uuid4().hex,
|
||||
'totalCoin': 450000
|
||||
'totalCoin': 450000,
|
||||
'giftName': '摩天大楼',
|
||||
'num': 1
|
||||
}
|
||||
sc_data = {
|
||||
**base_data,
|
||||
@@ -444,6 +459,7 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
self.send_message(Command.ADD_GIFT, gift_data)
|
||||
gift_data['id'] = uuid.uuid4().hex
|
||||
gift_data['totalCoin'] = 1245000
|
||||
gift_data['giftName'] = '小电视飞船'
|
||||
self.send_message(Command.ADD_GIFT, gift_data)
|
||||
|
||||
@property
|
||||
@@ -455,4 +471,4 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
try:
|
||||
self.write_message(body)
|
||||
except tornado.websocket.WebSocketClosedError:
|
||||
pass
|
||||
self.on_close()
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
'component', {
|
||||
'libraryName': 'element-ui',
|
||||
'styleLibraryName': 'theme-chalk'
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
15938
frontend/package-lock.json
generated
15938
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -20,8 +20,9 @@
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.7.0",
|
||||
"@vue/cli-plugin-eslint": "^3.7.0",
|
||||
"@vue/cli-service": "^3.7.0",
|
||||
"@vue/cli-service": "^4.2.2",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-plugin-component": "^1.1.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"vue-template-compiler": "^2.5.21"
|
||||
|
||||
@@ -2,9 +2,11 @@ import {mergeConfig} from '@/utils'
|
||||
|
||||
export const DEFAULT_CONFIG = {
|
||||
minGiftPrice: 7, // $1
|
||||
mergeSimilarDanmaku: true,
|
||||
showDanmaku: true,
|
||||
showGift: true,
|
||||
showGiftName: false,
|
||||
mergeSimilarDanmaku: true,
|
||||
mergeGift: true,
|
||||
maxNumber: 60,
|
||||
|
||||
blockGiftDanmaku: true,
|
||||
|
||||
@@ -34,18 +34,19 @@
|
||||
<paid-message :key="pinnedMessage.id" v-else
|
||||
class="style-scope yt-live-chat-ticker-renderer"
|
||||
:price="pinnedMessage.price" :avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName"
|
||||
:time="pinnedMessage.time" :content="pinnedMessage.content"
|
||||
:time="pinnedMessage.time" :content="showContent"
|
||||
></paid-message>
|
||||
</template>
|
||||
</yt-live-chat-ticker-renderer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as config from '@/api/config'
|
||||
import {formatCurrency} from '@/utils'
|
||||
import ImgShadow from './ImgShadow.vue'
|
||||
import LegacyPaidMessage from './LegacyPaidMessage.vue'
|
||||
import PaidMessage from './PaidMessage.vue'
|
||||
import * as constants from './constants'
|
||||
import {formatCurrency} from '@/utils'
|
||||
|
||||
export default {
|
||||
name: 'Ticker',
|
||||
@@ -55,7 +56,11 @@ export default {
|
||||
PaidMessage
|
||||
},
|
||||
props: {
|
||||
messages: Array
|
||||
messages: Array,
|
||||
showGiftName: {
|
||||
type: Boolean,
|
||||
default: config.DEFAULT_CONFIG.showGiftName
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -65,6 +70,18 @@ export default {
|
||||
pinnedMessage: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showContent() {
|
||||
if (!this.pinnedMessage) {
|
||||
return ''
|
||||
}
|
||||
if (this.pinnedMessage.type === constants.MESSAGE_TYPE_GIFT) {
|
||||
return constants.getGiftShowContent(this.pinnedMessage, this.showGiftName)
|
||||
} else {
|
||||
return constants.getShowContent(this.pinnedMessage)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.clearInterval(this.updateTimerId)
|
||||
},
|
||||
|
||||
@@ -18,10 +18,11 @@ export const GUARD_LEVEL_TO_TEXT = [
|
||||
]
|
||||
|
||||
export const MESSAGE_TYPE_TEXT = 0
|
||||
export const MESSAGE_TYPE_MEMBER = 1
|
||||
export const MESSAGE_TYPE_SUPER_CHAT = 2
|
||||
export const MESSAGE_TYPE_DEL = 3
|
||||
export const MESSAGE_TYPE_UPDATE = 4
|
||||
export const MESSAGE_TYPE_GIFT = 1
|
||||
export const MESSAGE_TYPE_MEMBER = 2
|
||||
export const MESSAGE_TYPE_SUPER_CHAT = 3
|
||||
export const MESSAGE_TYPE_DEL = 4
|
||||
export const MESSAGE_TYPE_UPDATE = 5
|
||||
|
||||
// 美元 -> 人民币 汇率
|
||||
const EXCHANGE_RATE = 7
|
||||
@@ -120,3 +121,17 @@ export function getPriceConfig (price) {
|
||||
}
|
||||
return PRICE_CONFIGS[PRICE_CONFIGS.length - 1]
|
||||
}
|
||||
|
||||
export function getShowContent(message) {
|
||||
if (message.translation) {
|
||||
return `${message.content}(${message.translation})`
|
||||
}
|
||||
return message.content
|
||||
}
|
||||
|
||||
export function getGiftShowContent(message, showGiftName) {
|
||||
if (!showGiftName) {
|
||||
return ''
|
||||
}
|
||||
return `Sent ${message.giftName}x${message.num}`
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<yt-live-chat-renderer class="style-scope yt-live-chat-app" style="--scrollbar-width:11px;" hide-timestamps
|
||||
@mousemove="refreshCantScrollStartTime"
|
||||
>
|
||||
<ticker class="style-scope yt-live-chat-renderer" :messages="paidMessages" :hidden="paidMessages.length === 0"></ticker>
|
||||
<ticker class="style-scope yt-live-chat-renderer" :messages="paidMessages" :showGiftName="showGiftName"
|
||||
:hidden="paidMessages.length === 0"
|
||||
></ticker>
|
||||
<yt-live-chat-item-list-renderer class="style-scope yt-live-chat-renderer" allow-scroll>
|
||||
<div ref="scroller" id="item-scroller" class="style-scope yt-live-chat-item-list-renderer animated" @scroll="onScroll">
|
||||
<div ref="itemOffset" id="item-offset" class="style-scope yt-live-chat-item-list-renderer" style="height: 0px;">
|
||||
@@ -16,6 +18,11 @@
|
||||
:authorType="message.authorType" :content="getShowContent(message)" :privilegeType="message.privilegeType"
|
||||
:repeated="message.repeated"
|
||||
></text-message>
|
||||
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_GIFT"
|
||||
class="style-scope yt-live-chat-item-list-renderer"
|
||||
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
||||
:time="message.time" :content="getGiftShowContent(message)"
|
||||
></paid-message>
|
||||
<legacy-paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_MEMBER"
|
||||
class="style-scope yt-live-chat-item-list-renderer"
|
||||
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
||||
@@ -58,6 +65,10 @@ export default {
|
||||
maxNumber: {
|
||||
type: Number,
|
||||
default: config.DEFAULT_CONFIG.maxNumber
|
||||
},
|
||||
showGiftName: {
|
||||
type: Boolean,
|
||||
default: config.DEFAULT_CONFIG.showGiftName
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -65,6 +76,7 @@ export default {
|
||||
document.head.appendChild(styleElement)
|
||||
return {
|
||||
MESSAGE_TYPE_TEXT: constants.MESSAGE_TYPE_TEXT,
|
||||
MESSAGE_TYPE_GIFT: constants.MESSAGE_TYPE_GIFT,
|
||||
MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER,
|
||||
MESSAGE_TYPE_SUPER_CHAT: constants.MESSAGE_TYPE_SUPER_CHAT,
|
||||
|
||||
@@ -118,12 +130,10 @@ export default {
|
||||
this.clearMessages()
|
||||
},
|
||||
methods: {
|
||||
getShowContent(message) {
|
||||
if (message.translation) {
|
||||
return `${message.content}(${message.translation})`
|
||||
}
|
||||
return message.content
|
||||
getGiftShowContent(message) {
|
||||
return constants.getGiftShowContent(message, this.showGiftName)
|
||||
},
|
||||
getShowContent: constants.getShowContent,
|
||||
|
||||
addMessage(message) {
|
||||
this.addMessages([message])
|
||||
@@ -159,14 +169,15 @@ export default {
|
||||
})
|
||||
return res
|
||||
},
|
||||
mergeSimilarGift(authorName, price) {
|
||||
mergeSimilarGift(authorName, price, giftName, num) {
|
||||
let res = false
|
||||
this.forEachRecentMessage(5, message => {
|
||||
if (message.type === constants.MESSAGE_TYPE_SUPER_CHAT
|
||||
&& message.content === ''
|
||||
if (message.type === constants.MESSAGE_TYPE_GIFT
|
||||
&& message.authorName === authorName
|
||||
&& message.giftName === giftName
|
||||
) {
|
||||
message.price += price
|
||||
message.num += num
|
||||
res = true
|
||||
return false
|
||||
}
|
||||
@@ -317,6 +328,7 @@ export default {
|
||||
for (let message of messageGroup) {
|
||||
switch (message.type) {
|
||||
case constants.MESSAGE_TYPE_TEXT:
|
||||
case constants.MESSAGE_TYPE_GIFT:
|
||||
case constants.MESSAGE_TYPE_MEMBER:
|
||||
case constants.MESSAGE_TYPE_SUPER_CHAT:
|
||||
this.handleAddMessage(message)
|
||||
|
||||
@@ -14,7 +14,9 @@ export default {
|
||||
roomId: 'Room ID',
|
||||
showDanmaku: 'Show messages',
|
||||
showGift: 'Show Super Chats',
|
||||
showGiftName: 'Show gift name',
|
||||
mergeSimilarDanmaku: 'Merge similar messages',
|
||||
mergeGift: 'Merge gifts',
|
||||
minGiftPrice: 'Min price of Super Chats to show (CNY)',
|
||||
maxNumber: 'Max number of messages',
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ export default {
|
||||
roomId: 'ルームID',
|
||||
showDanmaku: 'コメントを表示する',
|
||||
showGift: 'スーパーチャットと新メンバーを表示する',
|
||||
showGiftName: 'ギフト名を表示する',
|
||||
mergeSimilarDanmaku: '同じコメントを合併する',
|
||||
mergeGift: 'ギフトを合併する',
|
||||
minGiftPrice: '最低表示スーパーチャット価格(CNY)',
|
||||
maxNumber: '最大コメント数',
|
||||
|
||||
@@ -28,7 +30,7 @@ export default {
|
||||
blockUsers: 'ブロックユーザー',
|
||||
blockMedalLevel: 'ブロック勲章等級がx未満',
|
||||
|
||||
advanced: 'アドバンス',
|
||||
advanced: 'アドバンスド',
|
||||
autoTranslate: '自動翻訳コメントから日本語へ',
|
||||
|
||||
roomUrl: 'ルームのURL',
|
||||
|
||||
@@ -14,7 +14,9 @@ export default {
|
||||
roomId: '房间ID',
|
||||
showDanmaku: '显示弹幕',
|
||||
showGift: '显示打赏和新舰长',
|
||||
showGiftName: '显示礼物名',
|
||||
mergeSimilarDanmaku: '合并相似弹幕',
|
||||
mergeGift: '合并礼物',
|
||||
minGiftPrice: '最低显示打赏价格(元)',
|
||||
maxNumber: '最大弹幕数',
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="version">
|
||||
v1.4.0
|
||||
v1.4.2
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</el-aside>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import VueI18n from 'vue-i18n'
|
||||
import {
|
||||
Aside, Autocomplete, Badge, Button, Col, ColorPicker, Container, Divider, Form, FormItem, Image,
|
||||
Input, Main, Menu, MenuItem, Message, Row, Scrollbar, Slider, Submenu, Switch, TabPane, Tabs, Tooltip
|
||||
} from 'element-ui'
|
||||
import axios from 'axios'
|
||||
|
||||
import App from './App.vue'
|
||||
@@ -23,8 +25,32 @@ if (process.env.NODE_ENV === 'development') {
|
||||
}
|
||||
|
||||
Vue.use(VueRouter)
|
||||
Vue.use(ElementUI)
|
||||
Vue.use(VueI18n)
|
||||
// 初始化element
|
||||
Vue.use(Aside)
|
||||
Vue.use(Autocomplete)
|
||||
Vue.use(Badge)
|
||||
Vue.use(Button)
|
||||
Vue.use(Col)
|
||||
Vue.use(ColorPicker)
|
||||
Vue.use(Container)
|
||||
Vue.use(Divider)
|
||||
Vue.use(Form)
|
||||
Vue.use(FormItem)
|
||||
Vue.use(Image)
|
||||
Vue.use(Input)
|
||||
Vue.use(Main)
|
||||
Vue.use(Menu)
|
||||
Vue.use(MenuItem)
|
||||
Vue.use(Row)
|
||||
Vue.use(Scrollbar)
|
||||
Vue.use(Slider)
|
||||
Vue.use(Submenu)
|
||||
Vue.use(Switch)
|
||||
Vue.use(TabPane)
|
||||
Vue.use(Tabs)
|
||||
Vue.use(Tooltip)
|
||||
Vue.prototype.$message = Message
|
||||
|
||||
Vue.config.ignoredElements = [
|
||||
/^yt-/
|
||||
|
||||
@@ -16,9 +16,15 @@
|
||||
<el-form-item :label="$t('home.showGift')">
|
||||
<el-switch v-model="form.showGift"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('home.showGiftName')">
|
||||
<el-switch v-model="form.showGiftName"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('home.mergeSimilarDanmaku')">
|
||||
<el-switch v-model="form.mergeSimilarDanmaku"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('home.mergeGift')">
|
||||
<el-switch v-model="form.mergeGift"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('home.minGiftPrice')">
|
||||
<el-input v-model.number="form.minGiftPrice" type="number" min="0"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<chat-renderer ref="renderer" :maxNumber="config.maxNumber"></chat-renderer>
|
||||
<chat-renderer ref="renderer" :maxNumber="config.maxNumber" :showGiftName="config.showGiftName"></chat-renderer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -43,6 +43,11 @@ export default {
|
||||
created() {
|
||||
this.updateConfig()
|
||||
this.wsConnect()
|
||||
// 提示用户已加载
|
||||
this.$message({
|
||||
message: 'Loaded',
|
||||
duration: '500'
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.isDestroying = true
|
||||
@@ -60,9 +65,11 @@ export default {
|
||||
cfg = mergeConfig(cfg, config.DEFAULT_CONFIG)
|
||||
|
||||
cfg.minGiftPrice = toInt(cfg.minGiftPrice, config.DEFAULT_CONFIG.minGiftPrice)
|
||||
cfg.mergeSimilarDanmaku = toBool(cfg.mergeSimilarDanmaku)
|
||||
cfg.showDanmaku = toBool(cfg.showDanmaku)
|
||||
cfg.showGift = toBool(cfg.showGift)
|
||||
cfg.showGiftName = toBool(cfg.showGiftName)
|
||||
cfg.mergeSimilarDanmaku = toBool(cfg.mergeSimilarDanmaku)
|
||||
cfg.mergeGift = toBool(cfg.mergeGift)
|
||||
cfg.maxNumber = toInt(cfg.maxNumber, config.DEFAULT_CONFIG.maxNumber)
|
||||
cfg.blockGiftDanmaku = toBool(cfg.blockGiftDanmaku)
|
||||
cfg.blockLevel = toInt(cfg.blockLevel, config.DEFAULT_CONFIG.blockLevel)
|
||||
@@ -153,20 +160,21 @@ export default {
|
||||
break
|
||||
}
|
||||
let price = data.totalCoin / 1000
|
||||
if (price < this.config.minGiftPrice) { // 丢人
|
||||
if (this.mergeSimilarGift(data.authorName, price, data.giftName, data.num)) {
|
||||
break
|
||||
}
|
||||
if (this.mergeSimilarGift(data.authorName, price)) {
|
||||
if (price < this.config.minGiftPrice) { // 丢人
|
||||
break
|
||||
}
|
||||
message = {
|
||||
id: data.id,
|
||||
type: constants.MESSAGE_TYPE_SUPER_CHAT,
|
||||
type: constants.MESSAGE_TYPE_GIFT,
|
||||
avatarUrl: data.avatarUrl,
|
||||
time: new Date(data.timestamp * 1000),
|
||||
authorName: data.authorName,
|
||||
price: price,
|
||||
time: new Date(data.timestamp * 1000),
|
||||
content: '' // 有了SC,礼物不需要内容了
|
||||
giftName: data.giftName,
|
||||
num: data.num
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -257,11 +265,11 @@ export default {
|
||||
}
|
||||
return this.$refs.renderer.mergeSimilarText(content)
|
||||
},
|
||||
mergeSimilarGift(authorName, price) {
|
||||
if (!this.config.mergeSimilarDanmaku) {
|
||||
mergeSimilarGift(authorName, price, giftName, num) {
|
||||
if (!this.config.mergeGift) {
|
||||
return false
|
||||
}
|
||||
return this.$refs.renderer.mergeSimilarGift(authorName, price)
|
||||
return this.$refs.renderer.mergeSimilarGift(authorName, price, giftName, num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,8 +268,8 @@ const EXAMPLE_MESSAGES = [
|
||||
{
|
||||
...legacyPaidMessageTemplate,
|
||||
id: (nextId++).toString(),
|
||||
authorName: '少年Pi',
|
||||
content: 'Welcome 少年Pi!'
|
||||
authorName: '进击的冰糖',
|
||||
content: 'Welcome 进击的冰糖!'
|
||||
},
|
||||
{
|
||||
...paidMessageTemplate,
|
||||
|
||||
@@ -174,9 +174,8 @@ def update_avatar_cache(user_id, avatar_url):
|
||||
|
||||
def _update_avatar_cache_in_memory(user_id, avatar_url):
|
||||
_avatar_url_cache[user_id] = avatar_url
|
||||
if len(_avatar_url_cache) > 50000:
|
||||
for _, key in zip(range(100), _avatar_url_cache):
|
||||
del _avatar_url_cache[key]
|
||||
while len(_avatar_url_cache) > 50000:
|
||||
_avatar_url_cache.pop(next(iter(_avatar_url_cache)), None)
|
||||
|
||||
|
||||
def _update_avatar_cache_in_database(user_id, avatar_url):
|
||||
|
||||
@@ -16,7 +16,8 @@ DbSession: Optional[Type[sqlalchemy.orm.Session]] = None
|
||||
def init(debug):
|
||||
cfg = config.get_config()
|
||||
global engine, DbSession
|
||||
engine = sqlalchemy.create_engine(cfg.database_url, echo=debug)
|
||||
# engine = sqlalchemy.create_engine(cfg.database_url, echo=debug)
|
||||
engine = sqlalchemy.create_engine(cfg.database_url)
|
||||
DbSession = sqlalchemy.orm.sessionmaker(bind=engine)
|
||||
|
||||
OrmBase.metadata.create_all(engine)
|
||||
|
||||
@@ -103,9 +103,8 @@ def _on_translate_done(key, future):
|
||||
if res is None:
|
||||
return
|
||||
_translate_cache[key] = res
|
||||
if len(_translate_cache) > 50000:
|
||||
for _, key in zip(range(100), _translate_cache):
|
||||
del _translate_cache[key]
|
||||
while len(_translate_cache) > 50000:
|
||||
_translate_cache.pop(next(iter(_translate_cache)), None)
|
||||
|
||||
|
||||
class TranslateProvider:
|
||||
@@ -122,13 +121,19 @@ class TranslateProvider:
|
||||
|
||||
class TencentTranslate(TranslateProvider):
|
||||
def __init__(self):
|
||||
# 过期时间1小时
|
||||
self._qtv = ''
|
||||
self._qtk = ''
|
||||
self._reinit_future = None
|
||||
# 连续失败的次数
|
||||
self._fail_count = 0
|
||||
self._cool_down_future = None
|
||||
|
||||
async def init(self):
|
||||
if self._reinit_future is not None:
|
||||
self._reinit_future.cancel()
|
||||
self._reinit_future = None
|
||||
|
||||
try:
|
||||
async with _http_session.get('https://fanyi.qq.com/') as r:
|
||||
if r.status != 200:
|
||||
@@ -143,14 +148,25 @@ class TencentTranslate(TranslateProvider):
|
||||
if m is None:
|
||||
logger.exception('TencentTranslate init failed: qtv not found')
|
||||
return False
|
||||
self._qtv = m[1]
|
||||
qtv = m[1]
|
||||
m = re.search(r"""\bqtk\s*=\s*['"](.+?)['"]""", html)
|
||||
if m is None:
|
||||
logger.exception('TencentTranslate init failed: qtk not found')
|
||||
return False
|
||||
self._qtk = m[1]
|
||||
qtk = m[1]
|
||||
|
||||
self._qtv = qtv
|
||||
self._qtk = qtk
|
||||
self._reinit_future = asyncio.ensure_future(self._reinit_coroutine())
|
||||
return True
|
||||
|
||||
async def _reinit_coroutine(self):
|
||||
try:
|
||||
await asyncio.sleep(55 * 60)
|
||||
await self.init()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
@property
|
||||
def is_available(self):
|
||||
return self._qtv != '' and self._qtk != ''
|
||||
@@ -195,7 +211,12 @@ class TencentTranslate(TranslateProvider):
|
||||
if data['errCode'] != 0:
|
||||
logger.warning('TencentTranslate failed: %d %s', data['errCode'], data['errMsg'])
|
||||
return None
|
||||
return ''.join(record['targetText'] for record in data['translate']['records'])
|
||||
res = ''.join(record['targetText'] for record in data['translate']['records'])
|
||||
if res == '' and text.strip() != '':
|
||||
# qtv、qtk过期
|
||||
logger.warning('TencentTranslate result is empty %s', data)
|
||||
return None
|
||||
return res
|
||||
|
||||
def _on_fail(self):
|
||||
self._fail_count += 1
|
||||
@@ -210,7 +231,7 @@ class TencentTranslate(TranslateProvider):
|
||||
while True:
|
||||
await asyncio.sleep(3 * 60)
|
||||
try:
|
||||
is_success = self.init()
|
||||
is_success = await self.init()
|
||||
except:
|
||||
logger.exception('TencentTranslate init error:')
|
||||
continue
|
||||
@@ -320,7 +341,7 @@ class YoudaoTranslate(TranslateProvider):
|
||||
while True:
|
||||
await asyncio.sleep(3 * 60)
|
||||
try:
|
||||
is_success = self.init()
|
||||
is_success = await self.init()
|
||||
except:
|
||||
logger.exception('YoudaoTranslate init error:')
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user