Files
DanMuRead/Demos/Python/main.py
T
2025-04-30 22:53:19 +08:00

246 lines
9.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio
import websockets
import json
import time
from datetime import datetime
from typing import Dict, Any
from colorama import init, Fore, Style
from entities import (
PackMsgType, Gender, LiveStats, LikeMsg, MemberMessage, UserSeqMsg,
GiftMsg, FansclubMsg, ShareMessage, ShareType
)
from parsers import MessageParser
'''
抖音直播间消息处理脚本
消息类型说明:
1[普通弹幕]|2[点赞消息]|3[进入直播间]|4[关注消息]|5[礼物消息]|6[统计消息]|7[粉丝团消息]|8[直播间分享]|9[下播]
全局统计变量:
Live_Total - 本场点赞数
Live_user - 本场总观看人数
Live_man - 本场男生人数
Live_woman - 本场女生人数
'''
# 初始化彩色输出
init(autoreset=True)
# 颜色定义
COLOR_MAP = {
PackMsgType.弹幕消息: Fore.CYAN, # 青色
PackMsgType.点赞消息: Fore.YELLOW, # 黄色
PackMsgType.进直播间: Fore.GREEN, # 绿色
PackMsgType.关注消息: Fore.MAGENTA, # 洋红色
PackMsgType.礼物消息: Fore.RED, # 红色
PackMsgType.直播间统计: Fore.BLUE, # 蓝色
PackMsgType.粉丝团消息: Fore.LIGHTBLUE_EX, # 浅蓝色
PackMsgType.直播间分享: Fore.LIGHTMAGENTA_EX, # 浅洋红色
PackMsgType.下播: Fore.RED, # 红色
}
# 初始化统计对象
live_stats = LiveStats()
def get_timestamp() -> str:
"""返回当前时间戳,格式:HH:MM:SS"""
return datetime.now().strftime("%H:%M:%S")
def print_colored_message(msg_type: PackMsgType, content: str, author: str = "未知") -> None:
"""打印带颜色的消息"""
color = COLOR_MAP.get(msg_type, Fore.WHITE)
timestamp = get_timestamp()
elapsed_time = "["+author+"]"
print(f"{color}{timestamp} {elapsed_time} {content}{Style.RESET_ALL}")
def get_msg_owner(msg) -> str:
"""统一获取消息所属的主播/房间信息"""
if hasattr(msg, 'Owner') and msg.Owner:
return msg.Owner.Nickname
elif hasattr(msg, 'WebRoomId') and msg.WebRoomId:
return msg.WebRoomId
elif hasattr(msg, 'RoomId') and msg.RoomId:
return msg.RoomId
return "未知"
# 处理函数:弹幕消息
def handle_danmaku(data_dict: Dict[str, Any]) -> None:
"""处理用户发送的弹幕消息"""
msg = MessageParser.parse_danmaku(data_dict)
gender_str = msg.User.gender_to_string()
content = f"[弹幕消息] [{gender_str}] {msg.User.Nickname}: {msg.Content}"
author = get_msg_owner(msg)
print_colored_message(PackMsgType.弹幕消息, content, author)
# 处理函数:点赞消息
def handle_dianzanku(data_dict: Dict[str, Any]) -> None:
"""处理用户点赞消息并更新全局统计"""
like_msg = MessageParser.parse_like(data_dict)
live_stats.total_likes = like_msg.Total
gender_str = like_msg.User.gender_to_string()
content = f"[点赞消息] [{gender_str}] {like_msg.User.Nickname} 为主播点了{like_msg.Count}个赞, 总点赞{like_msg.Total}"
author = get_msg_owner(like_msg)
print_colored_message(PackMsgType.点赞消息, content, author)
# 处理函数:进入直播间
def handle_userentry(data_dict: Dict[str, Any]) -> None:
"""处理用户进入直播间的消息,并根据性别更新统计"""
member_msg = MessageParser.parse_member(data_dict)
# 根据性别更新全局计数
if member_msg.User.Gender == Gender.男:
live_stats.male_users += 1
elif member_msg.User.Gender == Gender.女:
live_stats.female_users += 1
gender_str = member_msg.User.gender_to_string()
content = f"[进直播间] [{gender_str}] {member_msg.User.Nickname} $来了 直播间人数:{member_msg.CurrentCount}"
author = get_msg_owner(member_msg)
print_colored_message(PackMsgType.进直播间, content, author)
# 处理函数:关注消息
def handle_follow(data_dict: Dict[str, Any]) -> None:
"""处理用户关注主播的消息"""
follow_msg = MessageParser.parse_follow(data_dict)
gender_str = follow_msg.User.gender_to_string()
content = f"[关注消息] [{gender_str}] {follow_msg.User.Nickname} 关注了主播"
author = get_msg_owner(follow_msg)
print_colored_message(PackMsgType.关注消息, content, author)
# 处理函数:礼物消息
def handle_gift(data_dict: Dict[str, Any]) -> None:
"""处理用户赠送礼物的消息"""
gift_msg = MessageParser.parse_gift(data_dict)
gender_str = gift_msg.User.gender_to_string()
# 使用DiamondCount或GiftValue作为礼物价值
gift_value = gift_msg.DiamondCount or gift_msg.GiftValue
content = f"[礼物消息] [{gender_str}] {gift_msg.User.Nickname} 送出了 {gift_msg.GiftCount}个{gift_msg.GiftName}, 价值{gift_value}抖币"
author = get_msg_owner(gift_msg)
print_colored_message(PackMsgType.礼物消息, content, author)
# 处理函数:统计消息
def handle_statistics(data_dict: Dict[str, Any]) -> None:
"""处理直播间统计信息"""
stats_msg = MessageParser.parse_statistics(data_dict)
live_stats.total_users = int(stats_msg.TotalUserCount)
content = f"[直播间统计] 当前直播间人数 {stats_msg.OnlineUserCountStr}, 累计直播间人数 {stats_msg.TotalUserCountStr}万"
author = get_msg_owner(stats_msg)
print_colored_message(PackMsgType.直播间统计, content, author)
# 处理函数:粉丝团消息
def handle_fansclub(data_dict: Dict[str, Any]) -> None:
"""处理粉丝团相关消息"""
fansclub_msg = MessageParser.parse_fansclub(data_dict)
gender_str = fansclub_msg.User.gender_to_string()
if fansclub_msg.Type == 1:
action = "升级了"
content = f"[粉丝团消息] [{gender_str}] {fansclub_msg.User.Nickname} {action}{fansclub_msg.FansClubName}粉丝团到{fansclub_msg.Level}级"
else:
action = "加入了"
content = f"[粉丝团消息] [{gender_str}] {fansclub_msg.User.Nickname} {action}{fansclub_msg.FansClubName}粉丝团"
author = get_msg_owner(fansclub_msg)
print_colored_message(PackMsgType.粉丝团消息, content, author)
# 处理函数:直播间分享
def handle_share(data_dict: Dict[str, Any]) -> None:
"""处理用户分享直播间的消息"""
share_msg = MessageParser.parse_share(data_dict)
gender_str = share_msg.User.gender_to_string()
# 获取分享目标类型
share_type_str = share_msg.ShareType.name if hasattr(share_msg.ShareType, 'name') else "未知平台"
content = f"[直播间分享] [{gender_str}] {share_msg.User.Nickname} 分享了直播间到{share_type_str}"
author = get_msg_owner(share_msg)
print_colored_message(PackMsgType.直播间分享, content, author)
# 处理函数:直播结束
def handle_live_exit() -> None:
"""处理直播结束事件,显示并重置统计数据"""
content = f"[下播] 直播结束:累计观看 {live_stats.total_users} 人,累计点赞 {live_stats.total_likes},"\
f"男生 {live_stats.male_users} 女生 {live_stats.female_users}"
print_colored_message(PackMsgType.下播, content, "系统消息")
live_stats.reset()
print(f"直播统计数据已清空~")
async def process_message(message: str) -> None:
"""处理从WebSocket接收到的单条消息"""
try:
data = json.loads(message)
if "Type" not in data:
print("⚠️ 收到无效消息:缺少Type字段")
return
msg_type = PackMsgType(data['Type'])
# 检查是否需要处理Data字段
if msg_type == PackMsgType.下播:
handle_live_exit()
return
if "Data" not in data:
print(f"⚠️ 消息类型 {msg_type.name} 缺少Data字段")
return
try:
data_dict = json.loads(data['Data'])
# 消息处理映射表
handlers = {
PackMsgType.弹幕消息: handle_danmaku,
PackMsgType.点赞消息: handle_dianzanku,
PackMsgType.进直播间: handle_userentry,
PackMsgType.关注消息: handle_follow,
PackMsgType.礼物消息: handle_gift,
PackMsgType.直播间统计: handle_statistics,
PackMsgType.粉丝团消息: handle_fansclub,
PackMsgType.直播间分享: handle_share,
}
# 分发消息到相应处理器
if msg_type in handlers:
handlers[msg_type](data_dict)
except json.JSONDecodeError:
print(f"⚠️ 无法解析Data字段内容:{data['Data'][:100]}...")
except json.JSONDecodeError:
print(f"⚠️ 无效的JSON格式: {message[:100]}...")
except Exception as e:
print(f"❌ 消息处理错误: {str(e)}")
async def connect_and_print() -> None:
"""连接到WebSocket服务器并处理接收到的消息"""
uri = "ws://127.0.0.1:8888"
while True:
try:
print(f"正在连接到服务器 {uri}...")
async with websockets.connect(uri, ping_interval=None) as websocket:
print(f"✅ 已成功连接到服务器 {uri}")
while True:
message = await websocket.recv()
await process_message(message)
except websockets.exceptions.ConnectionClosed as e:
print(f"⚠️ 与服务器的连接已关闭: {e}")
except ConnectionRefusedError:
print(f"❌ 无法连接到服务器 {uri},5秒后重试...")
except Exception as e:
print(f"❌ 发生错误: {str(e)}")
# 连接断开后等待重连
await asyncio.sleep(5)
print("正在尝试重新连接...")
# 程序入口
if __name__ == "__main__":
print("启动抖音直播消息监听...")
asyncio.run(connect_and_print())