mirror of
https://github.com/xfgryujk/blivechat.git
synced 2024-12-26 04:41:40 +08:00
GUI插件添加消息处理
This commit is contained in:
parent
d6030aba9c
commit
8277c56224
@ -113,18 +113,18 @@ class MsgHandler(blcsdk.BaseHandler):
|
||||
|
||||
|
||||
def _get_or_add_room(room_id):
|
||||
ctx = _id_room_dict.get(room_id, None)
|
||||
if ctx is None:
|
||||
room = _id_room_dict.get(room_id, None)
|
||||
if room is None:
|
||||
if room_id is None:
|
||||
raise TypeError('room_id is None')
|
||||
ctx = _id_room_dict[room_id] = Room(room_id)
|
||||
return ctx
|
||||
room = _id_room_dict[room_id] = Room(room_id)
|
||||
return room
|
||||
|
||||
|
||||
def _del_room(room_id):
|
||||
ctx = _id_room_dict.pop(room_id, None)
|
||||
if ctx is not None:
|
||||
ctx.close()
|
||||
room = _id_room_dict.pop(room_id, None)
|
||||
if room is not None:
|
||||
room.close()
|
||||
|
||||
|
||||
class Room:
|
||||
|
21
plugins/native-ui/LICENSE
Normal file
21
plugins/native-ui/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 xfgryujk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
227
plugins/native-ui/listener.py
Normal file
227
plugins/native-ui/listener.py
Normal file
@ -0,0 +1,227 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import __main__
|
||||
import dataclasses
|
||||
import datetime
|
||||
import logging
|
||||
from typing import *
|
||||
|
||||
import blcsdk
|
||||
import blcsdk.models as sdk_models
|
||||
|
||||
logger = logging.getLogger('native-ui.' + __name__)
|
||||
|
||||
_msg_handler: Optional['MsgHandler'] = None
|
||||
_key_room_dict: Dict[sdk_models.RoomKey, 'Room'] = {}
|
||||
|
||||
|
||||
async def init():
|
||||
global _msg_handler
|
||||
_msg_handler = MsgHandler()
|
||||
blcsdk.set_msg_handler(_msg_handler)
|
||||
|
||||
# 创建已有的房间。这一步失败了也没关系,只是有消息时才会创建房间
|
||||
try:
|
||||
blc_rooms = await blcsdk.get_rooms()
|
||||
for blc_room in blc_rooms:
|
||||
if blc_room.room_id is not None:
|
||||
_get_or_add_room(blc_room.room_key, blc_room.room_id)
|
||||
except blcsdk.SdkError:
|
||||
pass
|
||||
|
||||
|
||||
def shut_down():
|
||||
blcsdk.set_msg_handler(None)
|
||||
|
||||
|
||||
class MsgHandler(blcsdk.BaseHandler):
|
||||
def on_client_stopped(self, client: blcsdk.BlcPluginClient, exception: Optional[Exception]):
|
||||
logger.info('blivechat disconnected')
|
||||
__main__.start_shut_down()
|
||||
|
||||
def _on_open_plugin_admin_ui(
|
||||
self, client: blcsdk.BlcPluginClient, message: sdk_models.OpenPluginAdminUiMsg, extra: sdk_models.ExtraData
|
||||
):
|
||||
pass
|
||||
|
||||
def _on_room_init(
|
||||
self, client: blcsdk.BlcPluginClient, message: sdk_models.RoomInitMsg, extra: sdk_models.ExtraData
|
||||
):
|
||||
if extra.is_from_plugin:
|
||||
return
|
||||
if message.is_success:
|
||||
_get_or_add_room(extra.room_key, extra.room_id)
|
||||
|
||||
def _on_del_room(self, client: blcsdk.BlcPluginClient, message: sdk_models.DelRoomMsg, extra: sdk_models.ExtraData):
|
||||
if extra.is_from_plugin:
|
||||
return
|
||||
if extra.room_id is not None:
|
||||
_del_room(extra.room_key)
|
||||
|
||||
def _on_add_text(self, client: blcsdk.BlcPluginClient, message: sdk_models.AddTextMsg, extra: sdk_models.ExtraData):
|
||||
if extra.is_from_plugin:
|
||||
return
|
||||
room = _get_or_add_room(extra.room_key, extra.room_id)
|
||||
room.add_danmaku(message.uid)
|
||||
|
||||
def _on_add_gift(self, client: blcsdk.BlcPluginClient, message: sdk_models.AddGiftMsg, extra: sdk_models.ExtraData):
|
||||
if extra.is_from_plugin:
|
||||
return
|
||||
room = _get_or_add_room(extra.room_key, extra.room_id)
|
||||
room.add_gift(GiftRecord(
|
||||
uid=str(message.uid), # TODO SDK的uid改成Open ID
|
||||
author_name=message.author_name,
|
||||
gift_name=message.gift_name,
|
||||
num=message.num,
|
||||
price=message.total_coin / 1000,
|
||||
))
|
||||
|
||||
def _on_add_member(
|
||||
self, client: blcsdk.BlcPluginClient, message: sdk_models.AddMemberMsg, extra: sdk_models.ExtraData
|
||||
):
|
||||
if extra.is_from_plugin:
|
||||
return
|
||||
room = _get_or_add_room(extra.room_key, extra.room_id)
|
||||
|
||||
# 消息里没有价格,这里按最低算
|
||||
if message.privilege_type == sdk_models.GuardLevel.LV1:
|
||||
guard_name = '舰长'
|
||||
price = 138
|
||||
elif message.privilege_type == sdk_models.GuardLevel.LV2:
|
||||
guard_name = '提督'
|
||||
price = 1998
|
||||
elif message.privilege_type == sdk_models.GuardLevel.LV3:
|
||||
guard_name = '总督'
|
||||
price = 19998
|
||||
else:
|
||||
guard_name = '未知舰队等级'
|
||||
price = 0
|
||||
guard_name += f'({message.unit})'
|
||||
|
||||
room.add_gift(GiftRecord(
|
||||
uid=str(message.uid), # TODO SDK的uid改成Open ID
|
||||
author_name=message.author_name,
|
||||
gift_name=guard_name,
|
||||
num=message.num,
|
||||
price=price,
|
||||
))
|
||||
|
||||
def _on_add_super_chat(
|
||||
self, client: blcsdk.BlcPluginClient, message: sdk_models.AddSuperChatMsg, extra: sdk_models.ExtraData
|
||||
):
|
||||
if extra.is_from_plugin:
|
||||
return
|
||||
room = _get_or_add_room(extra.room_key, extra.room_id)
|
||||
room.add_super_chat(SuperChatRecord(
|
||||
uid=str(message.uid), # TODO SDK的uid改成Open ID
|
||||
author_name=message.author_name,
|
||||
price=message.price,
|
||||
content=message.content,
|
||||
))
|
||||
|
||||
|
||||
def get_room(room_key: sdk_models.RoomKey):
|
||||
return _key_room_dict.get(room_key, None)
|
||||
|
||||
|
||||
def _get_or_add_room(room_key: sdk_models.RoomKey, room_id):
|
||||
room = _key_room_dict.get(room_key, None)
|
||||
if room is None:
|
||||
if room_id is None:
|
||||
raise TypeError('room_id is None')
|
||||
room = _key_room_dict[room_id] = Room(room_key, room_id)
|
||||
# TODO 打开房间窗口
|
||||
return room
|
||||
|
||||
|
||||
def _del_room(room_key: sdk_models.RoomKey):
|
||||
_key_room_dict.pop(room_key, None)
|
||||
# TODO 关闭房间窗口
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class SuperChatRecord:
|
||||
uid: str
|
||||
author_name: str
|
||||
price: float
|
||||
content: str
|
||||
time: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class GiftRecord:
|
||||
uid: str
|
||||
author_name: str
|
||||
gift_name: str
|
||||
num: int
|
||||
price: float
|
||||
time: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class PaidUserRecord:
|
||||
uid: str
|
||||
name: str
|
||||
price: float
|
||||
|
||||
|
||||
class Room:
|
||||
def __init__(self, room_key: sdk_models.RoomKey, room_id: int):
|
||||
self._room_key = room_key
|
||||
self._room_id = room_id
|
||||
|
||||
self._super_chats: List[SuperChatRecord] = []
|
||||
self._gifts: List[GiftRecord] = []
|
||||
self._uid_paid_user_dict: Dict[str, PaidUserRecord] = {}
|
||||
|
||||
self._danmaku_num = 0
|
||||
self._interact_uids: Set[str] = set()
|
||||
self._total_paid_price = 0
|
||||
|
||||
def add_danmaku(self, uid):
|
||||
self._danmaku_num += 1
|
||||
self._interact_uids.add(uid)
|
||||
|
||||
def add_super_chat(self, super_chat: SuperChatRecord):
|
||||
self._super_chats.append(super_chat)
|
||||
self._add_user_paid_price(PaidUserRecord(
|
||||
uid=super_chat.uid,
|
||||
name=super_chat.author_name,
|
||||
price=super_chat.price,
|
||||
))
|
||||
self._danmaku_num += 1
|
||||
self._interact_uids.add(super_chat.uid)
|
||||
self._total_paid_price += super_chat.price
|
||||
|
||||
def add_gift(self, gift: GiftRecord):
|
||||
# 尝试合并
|
||||
is_merged = False
|
||||
min_time_to_merge = gift.time - datetime.timedelta(seconds=10)
|
||||
for old_gift in reversed(self._gifts):
|
||||
if old_gift.time < min_time_to_merge:
|
||||
break
|
||||
if old_gift.uid == gift.uid and old_gift.gift_name == gift.gift_name:
|
||||
old_gift.num += gift.num
|
||||
old_gift.price += gift.price
|
||||
is_merged = True
|
||||
break
|
||||
|
||||
if not is_merged:
|
||||
self._gifts.append(gift)
|
||||
if gift.price > 0.:
|
||||
self._add_user_paid_price(PaidUserRecord(
|
||||
uid=gift.uid,
|
||||
name=gift.author_name,
|
||||
price=gift.price,
|
||||
))
|
||||
self._interact_uids.add(gift.uid)
|
||||
self._total_paid_price += gift.price
|
||||
|
||||
def _add_user_paid_price(self, paid_user: PaidUserRecord):
|
||||
old_paid_user = self._uid_paid_user_dict.get(paid_user.uid, None)
|
||||
if old_paid_user is None:
|
||||
old_paid_user = self._uid_paid_user_dict[paid_user.uid] = PaidUserRecord(
|
||||
uid=paid_user.uid,
|
||||
name=paid_user.name,
|
||||
price=0,
|
||||
)
|
||||
old_paid_user.price += paid_user.price
|
@ -3,13 +3,16 @@
|
||||
import asyncio
|
||||
import logging.handlers
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from typing import *
|
||||
|
||||
import wx
|
||||
import wxasync
|
||||
|
||||
import blcsdk
|
||||
import config
|
||||
import designer.ui_base
|
||||
import listener
|
||||
|
||||
logger = logging.getLogger('native-ui')
|
||||
|
||||
@ -26,18 +29,38 @@ async def main():
|
||||
|
||||
|
||||
async def init():
|
||||
init_signal_handlers()
|
||||
|
||||
init_logging()
|
||||
|
||||
global app
|
||||
app = wxasync.WxAsyncApp()
|
||||
await blcsdk.init()
|
||||
if not blcsdk.is_sdk_version_compatible():
|
||||
raise RuntimeError('SDK version is not compatible')
|
||||
|
||||
# TODO 测试
|
||||
frame = designer.ui_base.RoomFrameBase(None)
|
||||
frame.chat_web_view.LoadURL('http://localhost:12450/room/test?minGiftPrice=0&showGiftName=true&relayMessagesByServer=true&lang=zh')
|
||||
frame.paid_web_view.LoadURL('http://localhost:12450/room/test?showDanmaku=false&showGiftName=true&relayMessagesByServer=true&lang=zh')
|
||||
frame.Show()
|
||||
init_ui()
|
||||
await listener.init()
|
||||
|
||||
app.SetTopWindow(frame)
|
||||
|
||||
def init_signal_handlers():
|
||||
signums = (signal.SIGINT, signal.SIGTERM)
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
for signum in signums:
|
||||
loop.add_signal_handler(signum, start_shut_down)
|
||||
except NotImplementedError:
|
||||
def signal_handler(*args):
|
||||
asyncio.get_running_loop().call_soon(start_shut_down, *args)
|
||||
|
||||
# 不太安全,但Windows只能用这个
|
||||
for signum in signums:
|
||||
signal.signal(signum, signal_handler)
|
||||
|
||||
|
||||
def start_shut_down(*_args):
|
||||
if app is not None:
|
||||
app.ExitMainLoop()
|
||||
else:
|
||||
wx.Exit()
|
||||
|
||||
|
||||
def init_logging():
|
||||
@ -55,6 +78,11 @@ def init_logging():
|
||||
)
|
||||
|
||||
|
||||
def init_ui():
|
||||
global app
|
||||
app = wxasync.WxAsyncApp(clearSigInt=False)
|
||||
|
||||
|
||||
async def run():
|
||||
logger.info('Running event loop')
|
||||
await app.MainLoop()
|
||||
@ -62,7 +90,8 @@ async def run():
|
||||
|
||||
|
||||
async def shut_down():
|
||||
pass
|
||||
listener.shut_down()
|
||||
await blcsdk.shut_down()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
8
plugins/native-ui/plugin.json
Normal file
8
plugins/native-ui/plugin.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "原生UI",
|
||||
"version": "1.0.0",
|
||||
"author": "xfgryujk",
|
||||
"description": "提供托盘图标和用来看弹幕的窗口。支持置顶窗口和设置不透明度。支持付费消息分开显示。统计弹幕数、付费用户等信息",
|
||||
"run": "native-ui.exe",
|
||||
"enabled": true
|
||||
}
|
10
plugins/native-ui/ui/room.py
Normal file
10
plugins/native-ui/ui/room.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import designer.ui_base
|
||||
|
||||
|
||||
class RoomFrame(designer.ui_base.RoomFrameBase):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self.chat_web_view.LoadURL('http://localhost:12450/room/test?minGiftPrice=0&showGiftName=true&relayMessagesByServer=true&lang=zh')
|
||||
self.paid_web_view.LoadURL('http://localhost:12450/room/test?showDanmaku=false&showGiftName=true&relayMessagesByServer=true&lang=zh')
|
@ -156,10 +156,15 @@ class TaskQueue:
|
||||
q = self._queues[task.priority]
|
||||
|
||||
# 尝试合并
|
||||
try_merge_count = 0
|
||||
for old_task in reversed(q):
|
||||
if old_task.merge(task):
|
||||
return True
|
||||
|
||||
try_merge_count += 1
|
||||
if try_merge_count >= 5:
|
||||
break
|
||||
|
||||
# 没满直接push
|
||||
if (
|
||||
self._max_size is None
|
||||
|
Loading…
Reference in New Issue
Block a user