using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; using ProtoBuf; using BarrageGrab.Proxy; using BarrageGrab.Proxy.ProxyEventArgs; using BarrageGrab.Modles.ProtoEntity; namespace BarrageGrab { /// /// 本机Wss弹幕抓取器 /// public class WssBarrageGrab : IDisposable { //ISystemProxy proxy = new FiddlerProxy(); ISystemProxy proxy = new TitaniumProxy(); AppSetting appsetting = AppSetting.Current; /// /// 进入直播间 /// public event EventHandler> OnMemberMessage; /// /// 关注 /// public event EventHandler> OnSocialMessage; /// /// 聊天 /// public event EventHandler> OnChatMessage; /// /// 点赞 /// public event EventHandler> OnLikeMessage; /// /// 礼物 /// public event EventHandler> OnGiftMessage; /// /// 直播间统计 /// public event EventHandler> OnRoomUserSeqMessage; /// /// 直播间状态变更 /// public event EventHandler> OnControlMessage; /// /// 粉丝团消息 /// public event EventHandler> OnFansclubMessage; /// /// 代理 /// public ISystemProxy Proxy { get { return proxy; } } public WssBarrageGrab() { proxy.OnWebSocketData += Proxy_OnWebSocketData; proxy.OnFetchResponse += Proxy_OnFetchResponse; } public void Start() { proxy.Start(); } public void Dispose() { proxy.Dispose(); } //gzip解压缩 private byte[] Decompress(byte[] zippedData) { MemoryStream ms = new MemoryStream(zippedData); GZipStream compressedzipStream = new GZipStream(ms, CompressionMode.Decompress); MemoryStream outBuffer = new MemoryStream(); byte[] block = new byte[1024]; while (true) { int bytesRead = compressedzipStream.Read(block, 0, block.Length); if (bytesRead <= 0) break; else outBuffer.Write(block, 0, bytesRead); } compressedzipStream.Close(); return outBuffer.ToArray(); } //ws数据处理 private void Proxy_OnWebSocketData(object sender, WsMessageEventArgs e) { if (!appsetting.ProcessFilter.Contains(e.ProcessName)) return; var buff = e.Payload; if (buff.Length == 0) return; //如果需要Gzip解压缩,但是开头字节不符合Gzip特征字节 则不处理 if (e.NeedDecompress && buff[0] != 0x08) return; try { var enty = Serializer.Deserialize(new ReadOnlyMemory(buff)); if (enty == null) return; //检测包格式 if (!enty.Headers.Any(a => a.Key == "compress_type" && a.Value == "gzip")) return; byte[] allBuff; //解压gzip allBuff = e.NeedDecompress ? Decompress(enty.Payload) : enty.Payload; var response = Serializer.Deserialize(new ReadOnlyMemory(allBuff)); response.Messages.ForEach(f => DoMessage(f, e.ProcessName)); } catch (Exception ex) { Logger.LogError(ex, $"处理弹幕数据包时出错:{ex.Message}"); } } //http 数据处理 private void Proxy_OnFetchResponse(object sender, HttpResponseEventArgs e) { var payload = e.Payload; if (payload == null || payload.Length == 0) return; var response = Serializer.Deserialize(new ReadOnlyMemory(payload)); response.Messages.ForEach(f => { DoMessage(f, e.ProcessName); }); } //用于缓存接收过的消息ID,判断是否重复接收 Dictionary> msgDic = new Dictionary>(); //发送事件 private void DoMessage(Message msg, string processName) { List msgIdList; if (msgDic.ContainsKey(msg.Method)) { msgIdList = msgDic[msg.Method]; } else { msgIdList = new List(320); msgDic.Add(msg.Method, msgIdList); } if (msgIdList.Contains(msg.msgId)) { return; } msgIdList.Add(msg.msgId); //每种消息类型设置300容量应该足够,不太可能存在一条消息被挤出队列后再次出现 while (msgIdList.Count > 300) { msgIdList.RemoveAt(0); } try { switch (msg.Method) { //来了 case "WebcastMemberMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnMemberMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //关注 case "WebcastSocialMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnSocialMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //消息 case "WebcastChatMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnChatMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //点赞 case "WebcastLikeMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnLikeMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //礼物 case "WebcastGiftMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnGiftMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //直播间统计 case "WebcastRoomUserSeqMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnRoomUserSeqMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //直播间状态变更 case "WebcastControlMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnControlMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } //粉丝团消息 case "WebcastFansclubMessage": { var arg = Serializer.Deserialize(new ReadOnlyMemory(msg.Payload)); this.OnFansclubMessage?.Invoke(this, new RoomMessageEventArgs(processName, arg)); break; } default: break; } } catch (Exception ex) { return; } } public class RoomMessageEventArgs : EventArgs where T : class { /// /// 进程名 /// public string Process { get; set; } /// /// 消息 /// public T Message { get; set; } public RoomMessageEventArgs() { } public RoomMessageEventArgs(string process, T data) { this.Process = process; this.Message = data; } } } }