diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5cc0821 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "blivedm"] + path = blivedm + url = https://github.com/xfgryujk/blivedm.git diff --git a/blivedm b/blivedm new file mode 160000 index 0000000..f755468 --- /dev/null +++ b/blivedm @@ -0,0 +1 @@ +Subproject commit f755468908586d39a3325e9f1b0eced42a37ecc2 diff --git a/chat.py b/chat.py new file mode 100644 index 0000000..ca1d964 --- /dev/null +++ b/chat.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +import enum +import json +from typing import * + +import tornado.websocket + +import blivedm.blivedm as blivedm + + +class Command(enum.IntEnum): + JOIN_ROOM = 0 + ADD_TEXT = 1 + ADD_GIFT = 2 + ADD_VIP = 3 + + +class Room(blivedm.BLiveClient): + def __init__(self, room_id): + super().__init__(room_id) + self.future = None + self.clients: List['ChatHandler'] = [] + + def start(self): + self.future = self.run() + + def stop(self): + if self.future is not None: + self.future.cancel() + + def send_message(self, cmd, data): + body = json.dumps({'cmd': cmd, 'data': data}) + for client in self.clients: + client.write_message(body) + + async def _on_get_danmaku(self, content, user_name): + # TODO + data = { + 'content': content, + 'authorName': user_name + } + self.send_message(Command.ADD_TEXT, data) + + +class RoomManager: + def __init__(self): + self._rooms: Dict[int, Room] = {} + + def add_client(self, room_id, client): + if room_id in self._rooms: + room = self._rooms[room_id] + else: + room = Room(room_id) + self._rooms[room_id] = room + room.start() + room.clients.append(client) + + def del_client(self, room_id, client: 'ChatHandler'): + if room_id not in self._rooms: + return + room = self._rooms[room_id] + room.clients.remove(client) + if not room.clients: + room.stop() + del self._rooms[room_id] + + +room_manager = RoomManager() + + +# noinspection PyAbstractClass +class ChatHandler(tornado.websocket.WebSocketHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.room_id = None + + def on_message(self, message): + if self.room_id is not None: + return + body = json.loads(message) + if body['cmd'] == Command.JOIN_ROOM: + room_id = body['data']['roomId'] + room_manager.add_client(room_id, self) + + def on_close(self): + if self.room_id is not None: + room_manager.del_client(self.room_id, self) + + # 测试用 + def check_origin(self, origin): + return True diff --git a/frontend/src/components/Room.vue b/frontend/src/components/Room.vue index a2084fe..683e6a6 100644 --- a/frontend/src/components/Room.vue +++ b/frontend/src/components/Room.vue @@ -1,9 +1,496 @@ + + diff --git a/frontend/src/components/TextMessage.vue b/frontend/src/components/TextMessage.vue new file mode 100644 index 0000000..a65c5e7 --- /dev/null +++ b/frontend/src/components/TextMessage.vue @@ -0,0 +1,24 @@ + + + diff --git a/main.py b/main.py index 8201a62..b5b0c54 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,8 @@ import os import tornado.ioloop import tornado.web +import chat + WEB_ROOT = os.path.join(os.path.dirname(__file__), 'frontend', 'dist') @@ -17,10 +19,11 @@ class MainHandler(tornado.web.StaticFileHandler): def main(): app = tornado.web.Application([ + (r'/chat', chat.ChatHandler), (r'/((css|img|js)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}), (r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}), (r'/.*', MainHandler, {'path': WEB_ROOT}) - ]) + ], websocket_ping_interval=30) app.listen(80, '127.0.0.1') tornado.ioloop.IOLoop.current().start()