Light12306/ChatRoomServer.Main/ChatSession.cs

429 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<ChatSession>
{
static ILog _log = ChatServer.GetLogger(typeof(ChatSession).Name);
DateTime _connecTime = DateTime.Now;
long _connectId;
bool _isAdmin;
/// <summary>
/// 路径分割
/// </summary>
public string[] PathSegements { get; private set; }
/// <summary>
/// 命令
/// </summary>
public string Command { get; private set; }
public string UserName { get; private set; }
public string NickName { get; private set; }
/// <summary>
/// 获得或设置房间
/// </summary>
public RoomContainer RoomContainer { get; set; }
/// <summary>
/// 满足最低多少举报人数时,会自动处理
/// </summary>
public int AbuseReportLimit
{
get
{
var room = RoomContainer.SessionCount;
if (room < 40)
return 3;
else if (room < 80)
return 4;
return 5;
}
}
/// <summary>
/// ID
/// </summary>
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;
}
/// <summary>
/// Called when [session started].
/// </summary>
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);
}
/// <summary>
/// Called when [session closed].
/// </summary>
/// <param name="reason">The reason.</param>
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<RequireSendMessageEventArgs> RequireSendMessage;
protected virtual void OnRequireSendMessage(MessageItem e, bool excludeSender)
{
RequireSendMessage?.Invoke(this, new RequireSendMessageEventArgs(excludeSender, e));
}
}
}