using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.WebSockets; using System.Text; using System.Threading.Tasks; using System.Timers; using ColorConsole; using Fleck; using Newtonsoft.Json; using BarrageGrab.Modles; using Newtonsoft.Json.Linq; using System.Net.Http; using Org.BouncyCastle.Asn1.Crmf; using System.IO.Compression; using System.IO; using BarrageGrab.Modles.JsonEntity; using BarrageGrab.Modles.ProtoEntity; namespace BarrageGrab { /// /// 弹幕服务 /// public class WsBarrageService { WebSocketServer socketServer; Dictionary socketList = new Dictionary(); //礼物计数缓存 Dictionary> giftCountCache = new Dictionary>(); Timer dieout = new Timer(10000); Timer giftCountTimer = new Timer(10000); ConsoleWriter console = new ConsoleWriter(); WssBarrageGrab grab = new WssBarrageGrab(); Appsetting Appsetting = Appsetting.Current; bool debug = false; /// /// 服务关闭后触发 /// public event EventHandler OnClose; public WsBarrageService() { #if DEBUG debug = true; #endif var socket = new WebSocketServer($"ws://0.0.0.0:{Appsetting.WsProt}"); socket.RestartAfterListenError = true;//异常重启 dieout.Elapsed += Dieout_Elapsed; giftCountTimer.Elapsed += GiftCountTimer_Elapsed; this.grab.OnChatMessage += Grab_OnChatMessage; this.grab.OnLikeMessage += Grab_OnLikeMessage; this.grab.OnMemberMessage += Grab_OnMemberMessage; this.grab.OnSocialMessage += Grab_OnSocialMessage; this.grab.OnSocialMessage += Grab_OnShardMessage; this.grab.OnGiftMessage += Grab_OnGiftMessage; this.grab.OnRoomUserSeqMessage += Grab_OnRoomUserSeqMessage; this.grab.OnFansclubMessage += Grab_OnFansclubMessage; ; this.grab.OnControlMessage += Grab_OnControlMessage; this.socketServer = socket; //dieout.Start(); giftCountTimer.Start(); } private void GiftCountTimer_Elapsed(object sender, ElapsedEventArgs e) { var now = DateTime.Now; var timeOutKeys = giftCountCache.Where(w => w.Value.Item2 < now.AddSeconds(-10) || w.Value == null).Select(s => s.Key).ToList(); //淘汰过期的礼物计数缓存 lock (giftCountCache) { timeOutKeys.ForEach(key => { giftCountCache.Remove(key); }); } } private bool CheckRoomId(long roomid) { if (Appsetting.RoomIds.Length == 0) return true; return Appsetting.RoomIds.Any(a => a == roomid); } //解析用户 private MsgUser GetUser(User data) { MsgUser user = new MsgUser() { DisplayId = data.displayId, ShortId = data.shortId, Gender = data.Gender, Id = data.Id, Level = data.Level, PayLevel = (int)(data.payGrade?.Level ?? 0), Nickname = data.Nickname, HeadImgUrl = data.avatarThumb.urlLists.FirstOrDefault() ?? "", SecUid = data.sec_uid, FollowerCount = data.followInfo.followerCount, FollowingCount = data.followInfo.followingCount, FollowStatus = data.followInfo.followStatus, }; user.FansClub = new FansClubInfo() { ClubName = "", Level = 0 }; if (data.fansClub != null && data.fansClub.Data != null) { user.FansClub.ClubName = data.fansClub.Data.clubName; user.FansClub.Level = data.fansClub.Data.Level; } return user; } //粉丝团 private void Grab_OnFansclubMessage(object sender, FansclubMessage e) { if (!CheckRoomId(e.Common.roomId)) return; var enty = new FansclubMsg() { MsgId = e.Common.msgId, Content = e.Content, RoomId = e.Common.roomId, Type = e.Type, User = GetUser(e.User) }; enty.Level = enty.User.FansClub.Level; Print(enty.User.GenderToString() + " " + enty.Content, ConsoleColor.Blue, BarrageMsgType.粉丝团消息); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.粉丝团消息); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //统计消息 private void Grab_OnRoomUserSeqMessage(object sender, RoomUserSeqMessage e) { if (!CheckRoomId(e.Common.roomId)) return; var enty = new UserSeqMsg() { MsgId = e.Common.msgId, OnlineUserCount = e.Total, TotalUserCount = e.totalUser, TotalUserCountStr = e.totalPvForAnchor, OnlineUserCountStr = e.onlineUserForAnchor, RoomId = e.Common.roomId, Content = $"当前直播间人数 {e.onlineUserForAnchor},累计直播间人数 {e.totalPvForAnchor}", User = null }; Print(enty.Content, ConsoleColor.Magenta, BarrageMsgType.直播间统计); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.直播间统计); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //礼物 private void Grab_OnGiftMessage(object sender, GiftMessage e) { if (!CheckRoomId(e.Common.roomId)) return; var key = e.giftId + "-" + e.groupId.ToString(); //判断礼物重复 if (e.repeatEnd == 1) { //清除缓存中的key if (e.groupId > 0 && giftCountCache.ContainsKey(key)) { lock (giftCountCache) { giftCountCache.Remove(key); } } return; } int lastCount = 0; int currCount = (int)e.repeatCount; var backward = currCount <= lastCount; if (currCount <= 0) currCount = 1; if (giftCountCache.ContainsKey(key)) { lastCount = giftCountCache[key].Item1; backward = currCount <= lastCount; if (!backward) { lock (giftCountCache) { giftCountCache[key] = Tuple.Create(currCount, DateTime.Now); } } } else { if (e.groupId > 0 && !backward) { lock (giftCountCache) { giftCountCache.Add(key, Tuple.Create(currCount, DateTime.Now)); } } } //比上次小,则说明先后顺序出了问题,直接丢掉,应为比它大的消息已经处理过了 if (backward) return; var count = currCount - lastCount; var enty = new GiftMsg() { MsgId = e.Common.msgId, RoomId = e.Common.roomId, Content = $"{e.User.Nickname} 送出 {e.Gift.Name} x {currCount} 个,增量{count}个", DiamondCount = e.Gift.diamondCount, RepeatCount = currCount, GiftCount = count, GroupId = e.groupId, GiftId = e.giftId, GiftName = e.Gift.Name, User = GetUser(e.User) }; Print($"{enty.User.GenderToString()} {enty.Content}", ConsoleColor.Red, BarrageMsgType.礼物消息); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.礼物消息); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //关注 private void Grab_OnSocialMessage(object sender, SocialMessage e) { if (!CheckRoomId(e.Common.roomId)) return; if (e.Action != 1) return; var enty = new Msg() { MsgId = e.Common.msgId, Content = $"{e.User.Nickname} 关注了主播", RoomId = e.Common.roomId, User = GetUser(e.User) }; Print($"{enty.User.GenderToString()} {enty.Content}", ConsoleColor.Yellow, BarrageMsgType.关注消息); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.关注消息); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //直播间分享 private void Grab_OnShardMessage(object sender, SocialMessage e) { if (!CheckRoomId(e.Common.roomId)) return; if (e.Action != 3) return; ShareType type = ShareType.未知; if (Enum.IsDefined(type.GetType(), int.Parse(e.shareTarget))) { type = (ShareType)int.Parse(e.shareTarget); } var enty = new ShareMessage() { MsgId = e.Common.msgId, Content = $"{e.User.Nickname} 分享了直播间到{type}", RoomId = e.Common.roomId, ShareType = type, User = GetUser(e.User) }; //shareTarget: (112:好友),(1微信)(2朋友圈)(3微博)(5:qq)(4:qq空间),shareType: 1 Print($"{enty.User.GenderToString()} {enty.Content}", ConsoleColor.DarkBlue, BarrageMsgType.直播间分享); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.直播间分享); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //来了 private void Grab_OnMemberMessage(object sender, Modles.ProtoEntity.MemberMessage e) { if (!CheckRoomId(e.Common.roomId)) return; var enty = new Modles.JsonEntity.MemberMessage() { MsgId = e.Common.msgId, Content = $"{e.User.Nickname} 来了 直播间人数:{e.memberCount}", RoomId = e.Common.roomId, CurrentCount = e.memberCount, User = GetUser(e.User) }; Print($"{enty.User.GenderToString()} {enty.Content}", ConsoleColor.Green, BarrageMsgType.进直播间); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.进直播间); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //点赞 private void Grab_OnLikeMessage(object sender, LikeMessage e) { if (!CheckRoomId(e.Common.roomId)) return; var enty = new LikeMsg() { MsgId = e.Common.msgId, Count = e.Count, Content = $"{e.User.Nickname} 为主播点了{e.Count}个赞,总点赞{e.Total}", RoomId = e.Common.roomId, Total = e.Total, User = GetUser(e.User) }; Print($"{enty.User.GenderToString()} {enty.Content}", ConsoleColor.Cyan, BarrageMsgType.点赞消息); var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.点赞消息); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //弹幕 private void Grab_OnChatMessage(object sender, ChatMessage e) { if (!CheckRoomId(e.Common.roomId)) return; var enty = new Msg() { MsgId = e.Common.msgId, Content = e.Content, RoomId = e.Common.roomId, User = GetUser(e.User) }; Print($"{enty.User.GenderToString()} {enty.User.Nickname}: {enty.Content}", ConsoleColor.White, BarrageMsgType.弹幕消息); if (e.User.followInfo.followStatus != 1 && e.User.followInfo.followStatus != 0 && e.User.followInfo.followStatus != 2) { Console.WriteLine(e.User.followInfo.followStatus); } var pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.弹幕消息); var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } //直播间状态变更 private void Grab_OnControlMessage(object sender, ControlMessage e) { if (!CheckRoomId(e.Common.roomId)) return; BarrageMsgPack pack = null; //下播 if (e.Status == 3) { var enty = new Msg() { MsgId = e.Common.msgId, Content = "直播已结束", RoomId = e.Common.roomId, User = null }; Print($"直播已结束", ConsoleColor.DarkCyan, BarrageMsgType.下播); pack = new BarrageMsgPack(JsonConvert.SerializeObject(enty), BarrageMsgType.下播); } if (pack != null) { var json = JsonConvert.SerializeObject(pack); this.Broadcast(json); } } static int count = 0; private void Print(string msg, ConsoleColor color, BarrageMsgType bartype) { if (!Appsetting.PrintFilter.Any(a => a == bartype.GetHashCode())) return; if (Appsetting.PrintBarrage) { if (++count > 1000) { Console.Clear(); Console.WriteLine("控制台已清理"); } console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss")} [{bartype.ToString()}] " + msg + "\n", color); count = 0; } } private void Dieout_Elapsed(object sender, ElapsedEventArgs e) { var now = DateTime.Now; var dieoutKvs = socketList.Where(w => w.Value.LastPing.AddSeconds(dieout.Interval * 3) < now).ToList(); dieoutKvs.ForEach(f => f.Value.Socket.Close()); } private void Listen(IWebSocketConnection socket) { //客户端url string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort; if (!socketList.ContainsKey(clientUrl)) { socketList.Add(clientUrl, new UserState(socket)); console.WriteLine($"{DateTime.Now.ToLongTimeString()} 已经建立与[{clientUrl}]的连接", ConsoleColor.Green); } else { socketList[clientUrl].Socket = socket; } //接收指令 socket.OnMessage = (message) => { try { var cmdPack = JsonConvert.DeserializeObject(message); if (cmdPack == null) return; if (cmdPack.Cmd == CommandCode.Close) { this.Close(); } } catch (Exception) { } }; socket.OnClose = () => { socketList.Remove(clientUrl); console.WriteLine($"{DateTime.Now.ToLongTimeString()} 已经关闭与[{clientUrl}]的连接", ConsoleColor.Red); }; socket.OnPing = (data) => { socketList[clientUrl].LastPing = DateTime.Now; socket.SendPong(Encoding.UTF8.GetBytes("pong")); }; } /// /// 广播消息 /// /// public void Broadcast(string msg) { foreach (var user in socketList) { var socket = user.Value; socket.Socket.Send(msg); } //删除掉线的套接字 var offlines = socketList.Where(w => !w.Value.Socket.IsAvailable).Select(s => s.Key).ToList(); offlines.ForEach(key => socketList.Remove(key)); } /// /// 开始监听 /// public void StartListen() { this.grab.Start(); //启动代理 this.socketServer.Start(Listen);//启动监听 console.WriteLine($"{this.socketServer.Location} 弹幕服务已启动...", ConsoleColor.Green); Console.Title = $"抖音弹幕监听推送 [{this.socketServer.Location}]"; } /// /// 关闭服务器连接,并关闭系统代理 /// public void Close() { socketList.Values.ToList().ForEach(f => f.Socket.Close()); socketList.Clear(); socketServer.Dispose(); grab.Dispose(); this.OnClose?.Invoke(this, EventArgs.Empty); } class UserState { /// /// 套接字 /// public IWebSocketConnection Socket { get; set; } /// /// 上次发起心跳包时间 /// public DateTime LastPing { get; set; } = DateTime.Now; public UserState() { } public UserState(IWebSocketConnection socket) { Socket = socket; } } } }