mirror of
https://github.com/xfgryujk/blivechat.git
synced 2024-12-26 21:00:15 +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):
|
def _get_or_add_room(room_id):
|
||||||
ctx = _id_room_dict.get(room_id, None)
|
room = _id_room_dict.get(room_id, None)
|
||||||
if ctx is None:
|
if room is None:
|
||||||
if room_id is None:
|
if room_id is None:
|
||||||
raise TypeError('room_id is None')
|
raise TypeError('room_id is None')
|
||||||
ctx = _id_room_dict[room_id] = Room(room_id)
|
room = _id_room_dict[room_id] = Room(room_id)
|
||||||
return ctx
|
return room
|
||||||
|
|
||||||
|
|
||||||
def _del_room(room_id):
|
def _del_room(room_id):
|
||||||
ctx = _id_room_dict.pop(room_id, None)
|
room = _id_room_dict.pop(room_id, None)
|
||||||
if ctx is not None:
|
if room is not None:
|
||||||
ctx.close()
|
room.close()
|
||||||
|
|
||||||
|
|
||||||
class Room:
|
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 asyncio
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
import os
|
import os
|
||||||
|
import signal
|
||||||
import sys
|
import sys
|
||||||
from typing import *
|
from typing import *
|
||||||
|
|
||||||
|
import wx
|
||||||
import wxasync
|
import wxasync
|
||||||
|
|
||||||
|
import blcsdk
|
||||||
import config
|
import config
|
||||||
import designer.ui_base
|
import listener
|
||||||
|
|
||||||
logger = logging.getLogger('native-ui')
|
logger = logging.getLogger('native-ui')
|
||||||
|
|
||||||
@ -26,18 +29,38 @@ async def main():
|
|||||||
|
|
||||||
|
|
||||||
async def init():
|
async def init():
|
||||||
|
init_signal_handlers()
|
||||||
|
|
||||||
init_logging()
|
init_logging()
|
||||||
|
|
||||||
global app
|
await blcsdk.init()
|
||||||
app = wxasync.WxAsyncApp()
|
if not blcsdk.is_sdk_version_compatible():
|
||||||
|
raise RuntimeError('SDK version is not compatible')
|
||||||
|
|
||||||
# TODO 测试
|
init_ui()
|
||||||
frame = designer.ui_base.RoomFrameBase(None)
|
await listener.init()
|
||||||
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()
|
|
||||||
|
|
||||||
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():
|
def init_logging():
|
||||||
@ -55,6 +78,11 @@ def init_logging():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def init_ui():
|
||||||
|
global app
|
||||||
|
app = wxasync.WxAsyncApp(clearSigInt=False)
|
||||||
|
|
||||||
|
|
||||||
async def run():
|
async def run():
|
||||||
logger.info('Running event loop')
|
logger.info('Running event loop')
|
||||||
await app.MainLoop()
|
await app.MainLoop()
|
||||||
@ -62,7 +90,8 @@ async def run():
|
|||||||
|
|
||||||
|
|
||||||
async def shut_down():
|
async def shut_down():
|
||||||
pass
|
listener.shut_down()
|
||||||
|
await blcsdk.shut_down()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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]
|
q = self._queues[task.priority]
|
||||||
|
|
||||||
# 尝试合并
|
# 尝试合并
|
||||||
|
try_merge_count = 0
|
||||||
for old_task in reversed(q):
|
for old_task in reversed(q):
|
||||||
if old_task.merge(task):
|
if old_task.merge(task):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
try_merge_count += 1
|
||||||
|
if try_merge_count >= 5:
|
||||||
|
break
|
||||||
|
|
||||||
# 没满直接push
|
# 没满直接push
|
||||||
if (
|
if (
|
||||||
self._max_size is None
|
self._max_size is None
|
||||||
|
Loading…
Reference in New Issue
Block a user