193 lines
6.8 KiB
C#
193 lines
6.8 KiB
C#
using System.Collections.Generic;
|
|||
|
|
using Newtonsoft.Json.Linq;
|
||
|
|
|
||
|
|
namespace BarrageGrab.Modles.BiliProtoEntity
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// B站直播弹幕WS协议操作码
|
||
|
|
/// </summary>
|
||
|
|
public enum BiliOperation
|
||
|
|
{
|
||
|
|
/// <summary>客户端心跳</summary>
|
||
|
|
HEARTBEAT = 2,
|
||
|
|
/// <summary>服务器心跳回复 (人气值)</summary>
|
||
|
|
HEARTBEAT_REPLY = 3,
|
||
|
|
/// <summary>普通消息 (弹幕/礼物/进房等)</summary>
|
||
|
|
MESSAGE = 5,
|
||
|
|
/// <summary>客户端认证请求</summary>
|
||
|
|
AUTH = 7,
|
||
|
|
/// <summary>服务器认证回复</summary>
|
||
|
|
AUTH_REPLY = 8
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站WS协议版本 (影响Payload压缩方式)
|
||
|
|
/// </summary>
|
||
|
|
public enum BiliProtocolVersion
|
||
|
|
{
|
||
|
|
/// <summary>JSON明文</summary>
|
||
|
|
PLAIN = 0,
|
||
|
|
/// <summary>认证用JSON</summary>
|
||
|
|
PLAIN_AUTH = 1,
|
||
|
|
/// <summary>Zlib压缩 (内部可能含多个包)</summary>
|
||
|
|
ZLIB = 2,
|
||
|
|
/// <summary>Brotli压缩 (内部可能含多个包)</summary>
|
||
|
|
BROTLI = 3
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站WS协议帧头 (16字节, Big-Endian)
|
||
|
|
/// </summary>
|
||
|
|
public struct BiliSocketFrame
|
||
|
|
{
|
||
|
|
/// <summary>整个包长度 (header+body)</summary>
|
||
|
|
public uint PacketLength;
|
||
|
|
/// <summary>头长度 (固定16)</summary>
|
||
|
|
public ushort HeaderLength;
|
||
|
|
/// <summary>协议版本</summary>
|
||
|
|
public BiliProtocolVersion ProtocolVersion;
|
||
|
|
/// <summary>操作码</summary>
|
||
|
|
public BiliOperation Operation;
|
||
|
|
/// <summary>序列号 (固定1)</summary>
|
||
|
|
public uint SequenceId;
|
||
|
|
|
||
|
|
public const int HEADER_SIZE = 16;
|
||
|
|
|
||
|
|
/// <summary>从字节缓冲区解析帧头</summary>
|
||
|
|
public static bool TryParse(byte[] buffer, int offset, int count, out BiliSocketFrame frame)
|
||
|
|
{
|
||
|
|
frame = default(BiliSocketFrame);
|
||
|
|
if (buffer == null || count < HEADER_SIZE) return false;
|
||
|
|
// Big-Endian 解析
|
||
|
|
frame.PacketLength = ReadUInt32BE(buffer, offset);
|
||
|
|
frame.HeaderLength = ReadUInt16BE(buffer, offset + 4);
|
||
|
|
frame.ProtocolVersion = (BiliProtocolVersion)ReadUInt16BE(buffer, offset + 6);
|
||
|
|
frame.Operation = (BiliOperation)ReadUInt32BE(buffer, offset + 8);
|
||
|
|
frame.SequenceId = ReadUInt32BE(buffer, offset + 12);
|
||
|
|
return frame.HeaderLength >= HEADER_SIZE && frame.PacketLength >= HEADER_SIZE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>帧体数据 (从 HeaderLength 开始到 PacketLength)</summary>
|
||
|
|
public static byte[] GetPayload(byte[] buffer, int offset, BiliSocketFrame frame)
|
||
|
|
{
|
||
|
|
int bodyLen = (int)frame.PacketLength - frame.HeaderLength;
|
||
|
|
if (bodyLen <= 0) return new byte[0];
|
||
|
|
var data = new byte[bodyLen];
|
||
|
|
System.Array.Copy(buffer, offset + frame.HeaderLength, data, 0, bodyLen);
|
||
|
|
return data;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static uint ReadUInt32BE(byte[] buf, int off)
|
||
|
|
{
|
||
|
|
return (uint)((buf[off] << 24) | (buf[off + 1] << 16) | (buf[off + 2] << 8) | buf[off + 3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static ushort ReadUInt16BE(byte[] buf, int off)
|
||
|
|
{
|
||
|
|
return (ushort)((buf[off] << 8) | buf[off + 1]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站弹幕消息 (cmd字段用于路由)
|
||
|
|
/// </summary>
|
||
|
|
public class BiliMessage
|
||
|
|
{
|
||
|
|
/// <summary>消息类型 (DANMU_MSG / SEND_GIFT / INTERACT_WORD 等)</summary>
|
||
|
|
public string Cmd { get; set; }
|
||
|
|
|
||
|
|
/// <summary>原始数据</summary>
|
||
|
|
public JObject Raw { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站弹幕消息 cmd 列表 (用于识别已知类型)
|
||
|
|
/// </summary>
|
||
|
|
public static class BiliCmd
|
||
|
|
{
|
||
|
|
public const string DANMU_MSG = "DANMU_MSG";
|
||
|
|
public const string SEND_GIFT = "SEND_GIFT";
|
||
|
|
public const string INTERACT_WORD = "INTERACT_WORD";
|
||
|
|
public const string LIKE_INFO_V3_CLICK = "LIKE_INFO_V3_CLICK";
|
||
|
|
public const string ONLINE_RANK_COUNT = "ONLINE_RANK_COUNT";
|
||
|
|
public const string WATCHED_CHANGE = "WATCHED_CHANGE";
|
||
|
|
public const string LIVE = "LIVE";
|
||
|
|
public const string PREPARING = "PREPARING";
|
||
|
|
public const string ROOM_REAL_TIME_MESSAGE_UPDATE = "ROOM_REAL_TIME_MESSAGE_UPDATE";
|
||
|
|
public const string SUPER_CHAT_MESSAGE = "SUPER_CHAT_MESSAGE";
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站弹幕消息(DANMU_MSG)简化结构
|
||
|
|
/// </summary>
|
||
|
|
public class BiliDanmuMsg
|
||
|
|
{
|
||
|
|
/// <summary>弹幕内容 (info[1])</summary>
|
||
|
|
public string Content { get; set; }
|
||
|
|
/// <summary>用户UID (info[2][0])</summary>
|
||
|
|
public long Uid { get; set; }
|
||
|
|
/// <summary>用户昵称 (info[2][1])</summary>
|
||
|
|
public string Uname { get; set; }
|
||
|
|
/// <summary>管理员? (info[2][2]==1)</summary>
|
||
|
|
public bool IsAdmin { get; set; }
|
||
|
|
/// <summary>主播? (info[2][4]存在)</summary>
|
||
|
|
public bool IsAnchor { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站礼物消息(SEND_GIFT.data)简化结构
|
||
|
|
/// </summary>
|
||
|
|
public class BiliGiftData
|
||
|
|
{
|
||
|
|
public long Uid { get; set; }
|
||
|
|
public string Uname { get; set; }
|
||
|
|
public string Face { get; set; }
|
||
|
|
public uint GiftId { get; set; }
|
||
|
|
public string GiftName { get; set; }
|
||
|
|
/// <summary>礼物数量</summary>
|
||
|
|
public uint Num { get; set; }
|
||
|
|
/// <summary>本批次礼物数 (连击时为本次增量)</summary>
|
||
|
|
public uint ComboNum { get; set; }
|
||
|
|
/// <summary>礼物连击总数</summary>
|
||
|
|
public uint SuperBatchNum { get; set; }
|
||
|
|
/// <summary>价格 (1 = 1000金瓜子)</summary>
|
||
|
|
public long Price { get; set; }
|
||
|
|
/// <summary>金币类型 gold=付费 silver=免费</summary>
|
||
|
|
public string CoinType { get; set; }
|
||
|
|
/// <summary>礼物图片</summary>
|
||
|
|
public string GifUrl { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站进房/关注消息(INTERACT_WORD.data)简化结构
|
||
|
|
/// </summary>
|
||
|
|
public class BiliInteractData
|
||
|
|
{
|
||
|
|
public long Uid { get; set; }
|
||
|
|
public string Uname { get; set; }
|
||
|
|
/// <summary>1=进入 2=关注 3=分享</summary>
|
||
|
|
public int MsgType { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站点赞消息(LIKE_INFO_V3_CLICK.data)简化结构
|
||
|
|
/// </summary>
|
||
|
|
public class BiliLikeData
|
||
|
|
{
|
||
|
|
public long Uid { get; set; }
|
||
|
|
public string Uname { get; set; }
|
||
|
|
public string LikeText { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// B站房间统计(ONLINE_RANK_COUNT.data / ROOM_REAL_TIME_MESSAGE_UPDATE.data)简化结构
|
||
|
|
/// </summary>
|
||
|
|
public class BiliRoomStats
|
||
|
|
{
|
||
|
|
/// <summary>在线人数</summary>
|
||
|
|
public long Online { get; set; }
|
||
|
|
/// <summary>看过人数</summary>
|
||
|
|
public long Watched { get; set; }
|
||
|
|
}
|
||
|
|
}
|