using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ChatRoomServer.Main { using ChatRoomServer.Db; using ChatRoomServer.Db.Entities; using ChatRoomServer.Main.Entities; using ChatRoomServer.Main.Room; using FSLib.Logs.Log4Net; using FSLib.Network.SuperSocket.Protocols.WebSocket; using FSLib.Network.SuperSocket.SocketBase; using FSLib.Network.SuperSocket.SocketBase.Config; class ChatSession : WebSocketSession { static ILog _log = ChatServer.GetLogger(typeof(ChatSession).Name); DateTime _connecTime = DateTime.Now; long _connectId; bool _isAdmin; /// /// 路径分割 /// public string[] PathSegements { get; private set; } /// /// 命令 /// public string Command { get; private set; } public string UserName { get; private set; } public string NickName { get; private set; } /// /// 获得或设置房间 /// public RoomContainer RoomContainer { get; set; } /// /// 满足最低多少举报人数时,会自动处理 /// public int AbuseReportLimit { get { var room = RoomContainer.SessionCount; if (room < 40) return 3; else if (room < 80) return 4; return 5; } } /// /// ID /// public string Id { get; private set; } bool IsBlocked() { DateTime? dt = null; if ((AppServer as ChatServer).BlockUsers?.TryGetValue(UserName, out dt) == true) { IsSessionBlocked = true; Block(dt); return true; } return false; } /// /// Called when [session started]. /// protected override void OnSessionStarted() { base.OnSessionStarted(); var path = Path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (path.Length >= 4) { Command = path[0]; Id = System.Net.WebUtility.UrlDecode(path[1]); UserName = System.Net.WebUtility.UrlDecode(path[2]); NickName = System.Net.WebUtility.UrlDecode(path[3]); } PathSegements = path; _isAdmin = ChatServer.PresetAdministrators.Contains(UserName); if (IsBlocked()) return; //更新统计 var db = new ChatDb(); BlockUser blockRule = null; var user = db.Users.FirstOrDefault(s => s.UserName == UserName); if (user == null) { user = new User() { UserName = UserName, NickName = NickName, FirstConnect = DateTime.Now, Status = UserStatus.Normal }; db.Users.Add(user); } user.LastConnect = DateTime.Now; var ucl = new UserConnectLog() { UserName = UserName, ConnectTime = DateTime.Now, RoomID = Id, Ip = RemoteEndPoint.ToString() }; db.UserConnectLogs.Add(ucl); db.SaveChanges(); _connectId = ucl.Id; } public bool IsSessionBlocked { get; private set; } public void Block(DateTime? unblockTime) { TrySend(new MessageItem(SystemMessageType.OperationBlocked, content: "很抱歉,您的账户已经被封锁,暂时无法在聊天室暂住。封锁解除时间:" + (unblockTime == null ? "无限期" : unblockTime.Value.ToString()))); Close(CloseReason.TimeOut); } /// /// Called when [session closed]. /// /// The reason. protected override void OnSessionClosed(CloseReason reason) { base.OnSessionClosed(reason); //更新统计 if (_connectId > 0) { var elapsed = (int)(DateTime.Now - _connecTime).TotalSeconds; var db = new ChatDb(); db.Database.ExecuteSqlCommand("UPDATE Chat_UserConnectLog SET DisconnectTime=GETDATE(), ElapsedTime={1} WHERE Id={0}", _connectId, elapsed); db.Database.ExecuteSqlCommand("UPDATE Chat_User SET OnlineTime=OnlineTime+{1} WHERE UserName={0}", UserName, elapsed); } } public bool TrySend(MessageItem message) { var buffer = message.ToBuffer(); var value = TrySend(buffer, 0, buffer.Length); if (!value) { _log.Warn($"数据发送失败!连接ID={SessionID}"); } return value; } public void ProcessData(byte[] data) { if (IsBlocked()) return; MessageItem item; try { item = MessageItem.Decode(data); } catch (Exception) { return; } if (item.From == null || item.From.UserName.IsNullOrEmpty()) { item.From = new MessageUser(UserName, NickName); } if (item.SystemMessage) { ProcessSystemMessage(item); } else { ProcessUserMessage(item); } } void ProcessSystemMessage(MessageItem item) { var username = item.From?.UserName; if (!username.IsNullOrEmpty() && username != UserName) { //TODO 个别情况下,重新登录会导致登录用户名和当前连接用户名不匹配 TrySend(new MessageItem(SystemMessageType.OperationBlocked, content: "很抱歉,当前状态连接有误,请重新进入聊天室。")); return; } switch (item.SysMsgType) { case SystemMessageType.UserEnter: break; case SystemMessageType.UserExit: break; case SystemMessageType.Disconnect: break; case SystemMessageType.UpdateStat: break; case SystemMessageType.SendFailed: break; case SystemMessageType.UpdateOnlineCount: break; default: break; } } void ProcessUserMessage(MessageItem item) { switch (item.UserMsgType) { case UserMessageType.Send: //check username if (item.ToUsers != null && item.ToUsers.Length > 0) { var notOnlineUsers = item.ToUsers.Where(x => !RoomContainer.IsUserInRoom(x.UserName)).ToArray(); if (!notOnlineUsers.IsEmpty()) { item.Content += $"【系统提示:用户 {notOnlineUsers.Select(s => s.NickName).JoinAsString("、")} 已不在线。】"; } } //log to db var db = new ChatDb(); var msg = new MsgLog() { AtUser = item.ToUsers?.Select(s => s.UserName).JoinAsString(";"), Color = item.Color, Image = item.Images?.JoinAsString(";"), Content = item.Content, Ip = RemoteEndPoint.ToString(), RoomId = PathSegements[1], SendTime = item.Time, UserName = UserName }; db.MsgLogs.Add(msg); db.SaveChanges(); item.Id = msg.Id; //更新统计 db.Database.ExecuteSqlCommand("UPDATE Chat_User SET LastSend=GETDATE(), FirstSend=ISNULL(FirstSend, GETDATE()), SendTimes=SendTimes+1 WHERE UserName={0}", UserName); OnRequireSendMessage(item, false); break; case UserMessageType.Receive: break; case UserMessageType.ReportAbuse: ProcessReport(item.Content.ToInt64()); break; default: break; } } async void ProcessReport(long msgid) { if (msgid <= 0) return; var db = new ChatDb(); //查找当前用户资料 var currentUser = db.Users.Find(UserName); if (currentUser.DisableAbuseReport) { TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: false, content: "很抱歉,管理员已暂时关闭了您的举报权限。")); return; } if (currentUser.AbuseReportSuccRate < 0.1) { TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: false, content: "很抱歉,您当前举报成功率过低(<0.2),系统暂时不接受您新的举报。已有举报未通过审核时,请等待管理员审核。")); return; } //此消息是否已经被举报过了? if (db.AbuseReports.Any(s => s.TargetId == msgid && s.ReportUser == UserName)) { //重复举报 TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: false, content: "这条消息您已经举报过哦,请不要重复举报消息。")); return; } var msg = db.MsgLogs.Find(msgid); if (msg == null) return; var dispName = db.Users.Find(msg.UserName)?.NickName ?? ""; //是否已经有屏蔽记录了? currentUser.AbuseReportCount++; var currentRule = db.GetBlockRuleForUser(msg.UserName).LastOrDefault(); if (currentRule != null) { if (currentRule.UnblockTime != null) { currentUser.AbuseReportSuccCount++; db.AbuseReports.Add(new AbuseReport() { ReportTime = DateTime.Now, ReportUser = UserName, Status = ReportState.AutoProcessed, TargetId = msgid, TargetUser = msg.UserName }); //如果是隔期解开的,那么将会延长30分钟 var extra = _isAdmin ? 0 : 30; var extraDesc = extra == 0 ? "永久" : $"{extra} 分钟"; currentRule.UnblockTime = extra == 0 ? null : (DateTime?)currentRule.UnblockTime.Value.AddMinutes(_isAdmin ? 300 : 15); TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"您已举报成功。由于您的添砖加瓦,用户【{dispName}({msg.UserName})】将会在小黑屋中多享受 {extraDesc}的幸福时光。(您已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})")); OnRequireSendMessage(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"{currentUser.NickName} 已经举报成功。由于TA的添砖加瓦,用户【{dispName}({msg.UserName})】将会在小黑屋中多享受 {extraDesc}的幸福时光。({currentUser.NickName} 已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})"), true); db.SaveChanges(); } else { TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"无需举报,用户【{dispName}({msg.UserName})】已经暂时不会从小黑屋里粗来了")); } return; } //没有处于屏蔽状态,则 var rlog = new AbuseReport() { ReportTime = DateTime.Now, ReportUser = UserName, Status = _isAdmin ? ReportState.AutoProcessed : ReportState.Submited, TargetId = msgid, TargetUser = msg.UserName }; db.AbuseReports.Add(rlog); db.SaveChanges(); //自动处理 //如果是管理员,则自动禁言两个小时 var blocked = false; var currentReportCount = 0; DateTime? unblockTime = null; if (_isAdmin) { currentUser.AbuseReportSuccCount++; currentRule = new BlockUser() { BlockTime = DateTime.Now, UserName = msg.UserName, UnblockTime = null }; db.BlockUsers.Add(currentRule); unblockTime = currentRule.UnblockTime; TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"您已将用户【{dispName}({msg.UserName})】请到小黑屋里永享幸福。如果有更多人举报,呆的时间将会延长。(您已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})")); OnRequireSendMessage(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"用户【{dispName}({msg.UserName})】已经被管理员 {currentUser.NickName} 请到小黑屋里永享幸福。({currentUser.NickName} 已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})"), true); //TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"您已将用户【{dispName}({msg.UserName})】请到小黑屋里享受幸福至少五个小时。如果有更多人举报,呆的时间将会延长。(您已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})")); //OnRequireSendMessage(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"用户【{dispName}({msg.UserName})】已经被管理员 {currentUser.NickName} 请到小黑屋里享受幸福至少五个小时。如果有更多人举报,呆的时间将会延长。({currentUser.NickName} 已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})"), true); blocked = true; } else if ((currentReportCount = db.GetUserAbuseReportCountInTime(msg.UserName, 20)) >= AbuseReportLimit) { currentUser.AbuseReportSuccCount++; currentRule = new BlockUser() { BlockTime = DateTime.Now, UserName = msg.UserName, UnblockTime = DateTime.Now.AddMinutes(30) }; db.BlockUsers.Add(currentRule); unblockTime = currentRule.UnblockTime; TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"您的举报已经触发自动处理规则,用户【{dispName}({msg.UserName})】将被请到小黑屋里享受幸福至少十五分钟。(您已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})")); OnRequireSendMessage(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"【{currentUser.NickName}】 对【{dispName}({msg.UserName})】的举报已经满足自动处理规则,{dispName} 将会被请到小黑屋里享受幸福至少十五分钟。如果有更多人举报,呆的时间将会延长。({currentUser.NickName} 已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})"), true); blocked = true; } else { TrySend(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"举报成功,等待管理员审核。用户【{dispName}({msg.UserName})】在过去的十五分钟时间内已经被举报 {currentReportCount} 次,如果达到五次将会被自动封禁。(您已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})")); OnRequireSendMessage(new MessageItem(SystemMessageType.ReportAbuseResult, success: true, content: $"【{currentUser.NickName}】 对【{dispName}({msg.UserName})】的举报已记录。用户【{dispName}({msg.UserName})】在过去的十五分钟时间内已经被举报 {currentReportCount} 次,如果达到五次将会被自动封禁。({currentUser.NickName} 已举报 {currentUser.AbuseReportCount}次,成功 {currentUser.AbuseReportSuccCount}次,成功率 {currentUser.AbuseReportSuccRate:P1})"), true); } await db.SaveChangesAsync(); //即时封锁 if (blocked) { //自动标记之前的举报 await db.MarkProcessedAbuseReportAsync(msg.UserName, 3); (AppServer as ChatServer).AddBlockUser(msg.UserName, unblockTime); } } public event EventHandler RequireSendMessage; protected virtual void OnRequireSendMessage(MessageItem e, bool excludeSender) { RequireSendMessage?.Invoke(this, new RequireSendMessageEventArgs(excludeSender, e)); } } }