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 ColorConsole; 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; ConsoleWriter console = new ConsoleWriter(); //解包成功的域名缓存 List succPackHostNames = new List(); /// /// 进入直播间 /// 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)); if (!succPackHostNames.Contains(e.HostName)) { succPackHostNames.Add(e.HostName); SaveHostNameCache(); } response.Messages.ForEach(f => DoMessage(f,e.ProcessName)); } catch (Exception) { } } //http 数据处理 private void Proxy_OnFetchResponse(object sender, HttpResponseEventArgs e) { var payload = e.HttpClient.Response.Body; var response = Serializer.Deserialize(new ReadOnlyMemory(payload)); if (!succPackHostNames.Contains(e.HostName)) { succPackHostNames.Add(e.HostName); SaveHostNameCache(); } 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); if (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)); if (arg.Gift == null) { if (arg.giftId == 685) { arg.Gift = new GiftStruct() { Id = arg.giftId, Name = "粉丝灯牌", diamondCount = 1 }; } else if (arg.giftId == 3389) { arg.Gift = new GiftStruct() { Id = arg.giftId, Name = "欢乐盲盒", diamondCount = 10 }; } else if (arg.giftId == 4021) { arg.Gift = new GiftStruct() { Id = arg.giftId, Name = "欢乐拼图", diamondCount = 10 }; } else { console.WriteLine("未能识别的礼物ID:" + arg.giftId, ConsoleColor.Red); break; } } 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; } } //将成功解包的域名缓存到文件 private void SaveHostNameCache() { //获取程序运行目录 var baseDir = Directory.GetCurrentDirectory(); var fullPath = Path.Combine(baseDir, "成功解包域名缓存.txt"); try { //文件不存在则创建 if (!File.Exists(fullPath)) { File.Create(fullPath); } var text = File.ReadAllText(fullPath); var currentHosts = text.Split('\n').Where(w => !string.IsNullOrWhiteSpace(w)).Select(s => s.Trim().Trim('\r')).ToList(); var newHosts = succPackHostNames.Except(currentHosts).ToList(); //保存 if (newHosts.Count > 0) { File.AppendAllLines(fullPath, newHosts); } } catch (Exception ex) { Console.WriteLine("写入成功解包域名缓存失败:" + ex.Message, ConsoleColor.Red); } } 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; } } } }