You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

584 lines
20 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading;
using System.Collections.Concurrent;
using Sog;
using ProtoCSStruct;
using LitJson;
using System.Text.Json;
using static System.Net.Mime.MediaTypeNames;
namespace Chat
{
public class HeroSentimentSvc
{
public static string m_baseUrl;
public static int gameId;
public static string secretKey;
private static MD5 m_md5;
public static Object m_locker = new Object();
public static Object m_Banlocker = new Object();
public static JsonData m_ChatList;
public static JsonData m_BanLogList;
// 实时检测
public static string validateUrl = "/v2/api/content/validate";
// 上报聊天数据
public static string reportUrl = "/v2/api/message/receive";
// 上报封禁数据
public static string banLogUrl = "/v2/api/log/receive";
private static ConcurrentQueue<(PlayerOnChat, CSChatReq)> m_chatMsgWaitQueue = new ConcurrentQueue<(PlayerOnChat, CSChatReq)>();
private static ConcurrentQueue<SSCheckDirtyStringReq> m_checkMsgReqQueue = new ConcurrentQueue<SSCheckDirtyStringReq>();
private static bool m_isStart = false;
private static int m_threadCount = 3;
private static Thread[] m_threads = null;
private static long lastReportTime = 0;
private static long lastReportBanLogTime = 0;
public static void Init()
{
m_md5 = MD5.Create();
m_baseUrl = ChatServerUtils.GetServerConfig().sentimentUrl;
gameId = ChatServerUtils.GetServerConfig().sentimentGameId;
secretKey = ChatServerUtils.GetServerConfig().sentimentSecretKey;
m_ChatList = new JsonData();
m_ChatList.SetJsonType(JsonType.Array);
m_BanLogList = new JsonData();
m_BanLogList.SetJsonType(JsonType.Array);
if (ChatServerUtils.GetServerConfig().isUseHeroSentiment == 1)
{
Start();
}
}
private static string GetSign(string timeStamp)
{
if (m_md5 == null)
{
m_md5 = MD5.Create();
}
string singStr = secretKey + timeStamp;
byte[] strbyte = Encoding.UTF8.GetBytes(singStr);
byte[] md5Hash = m_md5.ComputeHash(strbyte);
string md5Str = BitConverter.ToString(md5Hash).Replace("-", "").ToLower();
return md5Str;
}
public static void OnHandleChatRealtimeValidate(PlayerOnChat player, ref CSChatReq req)
{
//将消息加入队列
m_chatMsgWaitQueue.Enqueue((player, req));
try
{
AddChatToList(player, ref req);
}
catch (Exception e)
{
TraceLog.Error("OnHandleChatRealtimeValidate.AddChatToList - error: ", e.Message);
}
}
public static void OnHandleDirtyChatCheck(ref SSCheckDirtyStringReq req)
{
m_checkMsgReqQueue.Enqueue(req);
}
private static void Start()
{
if (!m_isStart)
{
m_isStart = true;
m_threads = new Thread[m_threadCount];
for (int i = 0; i < m_threadCount; i++)
{
m_threads[i] = new Thread(WorkThreadFun);
//设置成后台线程,如果不是后台线程,线程不是主动关闭,则会一直运行,那怕进程主线程退出也没用
m_threads[i].IsBackground = true;
m_threads[i].Start();
}
var reportThread = new Thread(ThreadReport);
//设置成后台线程,如果不是后台线程,线程不是主动关闭,则会一直运行,那怕进程主线程退出也没用
reportThread.IsBackground = true;
reportThread.Start();
var reportBanLogThread = new Thread(ThreadReportBanLog);
//设置成后台线程,如果不是后台线程,线程不是主动关闭,则会一直运行,那怕进程主线程退出也没用
reportBanLogThread.IsBackground = true;
reportBanLogThread.Start();
}
}
private static void WorkThreadFun()
{
while (m_isStart)
{
bool haveData = false;
try
{
if (false == m_checkMsgReqQueue.IsEmpty && m_checkMsgReqQueue.TryDequeue(out var checkReq))
{
haveData = true;
HandleDirtyStringCheck(ref checkReq);
}
if (false == m_chatMsgWaitQueue.IsEmpty && m_chatMsgWaitQueue.TryDequeue(out var record))
{
haveData = true;
HandleChatRealtimeValidate(record.Item1, ref record.Item2);
}
}
catch (Exception ex)
{
TraceLog.Exception(ex);
}
if (!haveData)
{
Thread.Sleep(10);
}
}
}
/// <summary>
/// 脏词检查
/// </summary>
/// <param name="packet"></param>
public static void HandleDirtyStringCheck(ref SSCheckDirtyStringReq req)
{
if (ChatServerUtils.GetServerConfig().useWechatCheck)
{
bool pass = WechatCheckSvc.CheckDirtyName(req.Content.GetString());
SSCheckDirtyStringRes res = new SSCheckDirtyStringRes();
res.Result = pass ? 0 : 1;
res.Uid = req.Uid;
res.CheckSeq = req.CheckSeq;
res.ServerId = req.ServerId;
if (res.Result >= 0)
{
res.NewContent.SetString(req.Content.GetString());
}
ChatServerUtils.GetPacketSender().SendToServerByID(res.ServerId, (int)SSGameMsgID.CheckDirtyStringRes, ref res, res.Uid);
}
else
{
string newContent = GetDirtyStringResult(req.Content.GetString(), out int checkResult);
SSCheckDirtyStringRes res = new SSCheckDirtyStringRes();
res.Result = checkResult;
res.Uid = req.Uid;
res.CheckSeq = req.CheckSeq;
res.ServerId = req.ServerId;
if (res.Result >= 0)
{
res.NewContent.SetString(newContent);
}
ChatServerUtils.GetPacketSender().SendToServerByID(res.ServerId, (int)SSGameMsgID.CheckDirtyStringRes, ref res, res.Uid);
}
}
/// <summary>
/// 实时监测聊天
/// </summary>
/// <param name="packet"></param>
public static void HandleChatRealtimeValidate(PlayerOnChat player, ref CSChatReq req)
{
string newContent = GetDirtyStringResult(req.Message.GetString(), out int checkResult);
if (checkResult == 0)
{
ChatSvc.SendToChatMsg(player, ref req);
}
else if (checkResult == 1)
{
//有错误不让发
//req.Message.SetString(newContent);
//ChatSvc.SendToChatMsg(player, ref req);
CSChatRes res = new CSChatRes();
res.Ret = (int)CSErrCode.IncludeIllegalWord;
PlayerPacketSender.SendToPlayer(player, (int)CSGameMsgID.ChatRes, ref res);
TraceLog.Trace("HeroSentimentSvc.HandleChatRealtimeValidate send world player:{0} mess:{1} ret:{2}", player.UserID, req.Message.GetString(), checkResult);
}
else
{
CSChatRes res = new CSChatRes();
res.Ret = (int)CSErrCode.SysFailure;
PlayerPacketSender.SendToPlayer(player, (int)CSGameMsgID.ChatRes, ref res);
TraceLog.Error("HeroSentimentSvc.HandleChatRealtimeValidate error ret {0}", checkResult);
}
}
public static string GetDirtyStringResult(string oldStr, out int checkResult)
{
string str = "";
try
{
str = DealDirtyString(oldStr, out checkResult);
}
catch (Exception ex)
{
checkResult = -1;
str = "";
TraceLog.Exception(ex);
}
return str;
}
private static string DealDirtyString(string oldStr, out int checkResult)
{
string replace_content = "";
if (oldStr.Length == 0)
{
checkResult = 0;
return replace_content;
}
if (m_baseUrl == "" || m_baseUrl == null)
{
checkResult = -1;
TraceLog.Error("HeroSentimentSvc.DealDirtyString error baseUrl");
return replace_content;
}
var heardParams = new List<KeyValuePair<string, string>>();
string timeStamp = ChatServerUtils.GetTimeStamp(DateTime.Now).ToString();
heardParams.Add(new KeyValuePair<string, string>("gameId", Convert.ToString(gameId)));
heardParams.Add(new KeyValuePair<string, string>("sign", GetSign(timeStamp)));
heardParams.Add(new KeyValuePair<string, string>("timeStamp", timeStamp));
var contentParams = new Dictionary<string, string>();
contentParams["content"] = oldStr;
var content = new StringContent(
JsonSerializer.Serialize(contentParams),
Encoding.UTF8,
Application.Json);
string postUrl = m_baseUrl + validateUrl;
string ret = HttpUtils.HttpPost(postUrl, content, heardParams, out bool exception);
if (ret == null)
{
checkResult = -1;
TraceLog.Error("HeroSentimentSvc.DealDirtyString error HttpPost ret");
return replace_content;
}
JsonData jsonData = JsonMapper.ToObject(ret);
JsonData codeData = jsonData["code"];
int code = codeData != null ? (int)codeData : -1;
if (code != 0)
{
checkResult = -1;
TraceLog.Error("HeroSentimentSvc.DealDirtyString error code {0}", code);
return replace_content;
}
JsonData data = jsonData["data"];
checkResult = Convert.ToInt16(data["result"].ToString());
replace_content = data["replace_content"].ToString();
TraceLog.Trace("HeroSentimentSvc.DealDirtyString checkResult {0} str {1} ret {2} ", ret, oldStr, checkResult);
return replace_content;
}
public static void ThreadReport()
{
while (m_isStart)
{
long nowSecond = ChatServerUtils.GetTimeSecond();
if (m_ChatList.Count >= 50 || (nowSecond - lastReportTime) >= 5)
{
lastReportTime = nowSecond;
try
{
OnReportChatData();
}
catch (Exception e)
{
TraceLog.Error("WorkThreadFun.OnReportChatData.error: ", e.Message);
}
}
else
{
Thread.Sleep(10);
}
}
}
private static void AddChatToList(PlayerOnChat player, ref CSChatReq chatReq)
{
var chatMsg = new JsonData();
chatMsg["role_id"] = chatReq.Uid.ToString();
chatMsg["server_id"] = (int)player.RealmID;
chatMsg["role_name"] = player.Nick;
chatMsg["type"] = (int)chatReq.ChatChannelType;
chatMsg["message"] = chatReq.Message.GetString();
chatMsg["vip_level"] = player.roleBase.VipLevel;
chatMsg["level"] = player.roleBase.Level;
var extend_items = new JsonData();
extend_items["test"] = "testValue";
chatMsg["extend_items"] = extend_items;
lock (m_locker)
{
m_ChatList.Add(chatMsg);
}
}
private static void OnReportChatData()
{
if (m_ChatList.Count == 0)
{
return;
}
JsonData reportData = new JsonData();
string reportDataStr;
lock (m_locker)
{
reportData["chat_items"] = m_ChatList;
reportDataStr = reportData.ToJson();
m_ChatList.Clear();
}
var heardParams = new List<KeyValuePair<string, string>>();
string timeStamp = ChatServerUtils.GetTimeStamp(DateTime.Now).ToString();
heardParams.Add(new KeyValuePair<string, string>("gameId", Convert.ToString(gameId)));
heardParams.Add(new KeyValuePair<string, string>("sign", GetSign(timeStamp)));
heardParams.Add(new KeyValuePair<string, string>("timeStamp", timeStamp));
//var contentParams = new Dictionary<string, JsonData>();
//contentParams["chat_items"] = reportData;
var content = new StringContent(
reportDataStr,
Encoding.UTF8,
Application.Json);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
string postUrl = m_baseUrl + reportUrl;
string ret = HttpUtils.HttpPost(postUrl, content, heardParams, out bool exception);
if (ret == null)
{
TraceLog.Error("HeroSentimentSvc.OnReportChatData HttpGet return null query failed");
return;
}
JsonData jsonData = JsonMapper.ToObject(ret);
JsonData codeData = jsonData["code"];
int code = codeData != null ? (int)codeData : -1;
string msg = jsonData["msg"].ToString();
TraceLog.Trace("HeroSentimentSvc.OnReportChatData ret {0} code {1} msg {2}", ret, code, msg);
if (code != 0)
{
TraceLog.Error("HeroSentimentSvc.OnReportChatData: ret code : {0} msg :{1}", code, msg);
return;
}
}
public static void ThreadReportBanLog()
{
while (m_isStart)
{
long nowSecond = ChatServerUtils.GetTimeSecond();
if (m_BanLogList.Count >= 10 || (nowSecond - lastReportBanLogTime) >= 5)
{
lastReportBanLogTime = nowSecond;
try
{
OnReportBanLogData();
}
catch (Exception e)
{
TraceLog.Error("ThreadReportBanLog.OnReportBanLogData.error: ", e.Message);
}
}
else
{
Thread.Sleep(10);
}
}
}
public static void OnHandleChatBan(long uid, PlayerOnChat player, ref CSGagChatNotify req)
{
try
{
AddChatBanLogToList(uid, player, ref req);
}
catch (Exception e)
{
TraceLog.Error("OnHandleChatBan.AddChatBanLogToList - error: ", e.Message);
}
}
private static void AddChatBanLogToList(long uid, PlayerOnChat player, ref CSGagChatNotify req)
{
var chatMsg = new JsonData();
TraceLog.Trace("OnHandleChatBan.AddChatBanLogToList uid {0} GagEndTime {1} EnableType {2} EnableStr {3}", uid, req.GagEndTime, req.EnableType, req.EnableStr);
if (player == null) //TODO 没有上报区服和名字
{
chatMsg["server_id"] = 0;
chatMsg["role_id"] = uid.ToString();
chatMsg["role_name"] = "";
}
else
{
chatMsg["server_id"] = (int)player.RealmID;
chatMsg["role_id"] = player.UserID.ToString();
chatMsg["role_name"] = player.Nick;
}
chatMsg["ban_type"] = 2; //禁言
if (!string.IsNullOrEmpty(req.EnableStr.ToString()))
chatMsg["ban_reason"] = req.EnableStr.ToString();
else
chatMsg["ban_reason"] = req.EnableType.ToString();
chatMsg["ban_begin_time"] = ChatServerUtils.GetTimeSecond();
chatMsg["ban_end_time"] = req.GagEndTime;
lock (m_Banlocker)
{
m_BanLogList.Add(chatMsg);
}
}
public static void OnHandleSetFreezeTime(long uid, PlayerOnChat player, ref SSGMSetFreezeTimeRes req)
{
try
{
AddSetFreezeTimeBanLogToList(uid, player, ref req);
}
catch (Exception e)
{
TraceLog.Error("OnHandleChatBan.AddSetFreezeTimeBanLogToList - error: ", e.Message);
}
}
private static void AddSetFreezeTimeBanLogToList(long uid, PlayerOnChat player, ref SSGMSetFreezeTimeRes req)
{
var chatMsg = new JsonData();
TraceLog.Trace("OnHandleChatBan.AddSetFreezeTimeBanLogToList uid {0} FreezeTime {1} FreezeReason {2} FreezeReasonStr {3}", uid, req.FreezeTime, req.FreezeReason, req.FreezeReasonStr);
if (player == null) //TODO 没有上报区服和名字
{
chatMsg["server_id"] = 0;
chatMsg["role_id"] = uid.ToString();
chatMsg["role_name"] = "";
}
else
{
chatMsg["server_id"] = (int)player.RealmID;
chatMsg["role_id"] = player.UserID.ToString();
chatMsg["role_name"] = player.Nick;
}
chatMsg["ban_type"] = 1; //封号
if (!string.IsNullOrEmpty(req.FreezeReasonStr.ToString()))
chatMsg["ban_reason"] = req.FreezeReasonStr.ToString();
else
chatMsg["ban_reason"] = req.FreezeReason.ToString();
chatMsg["ban_begin_time"] = ChatServerUtils.GetTimeSecond();
chatMsg["ban_end_time"] = req.FreezeTime;
lock (m_Banlocker)
{
m_BanLogList.Add(chatMsg);
}
}
private static void OnReportBanLogData()
{
if (m_BanLogList.Count == 0)
{
return;
}
JsonData reportData = new JsonData();
string reportDataStr;
lock (m_Banlocker)
{
reportData["log_items"] = m_BanLogList;
reportDataStr = reportData.ToJson();
m_BanLogList.Clear();
}
var heardParams = new List<KeyValuePair<string, string>>();
string timeStamp = ChatServerUtils.GetTimeStamp(DateTime.Now).ToString();
heardParams.Add(new KeyValuePair<string, string>("gameId", Convert.ToString(gameId)));
heardParams.Add(new KeyValuePair<string, string>("sign", GetSign(timeStamp)));
heardParams.Add(new KeyValuePair<string, string>("timeStamp", timeStamp));
var content = new StringContent(
reportDataStr,
Encoding.UTF8,
Application.Json);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
string postUrl = m_baseUrl + banLogUrl;
string ret = HttpUtils.HttpPost(postUrl, content, heardParams, out bool exception);
if (ret == null)
{
TraceLog.Error("HeroSentimentSvc.OnReportBanLogData HttpGet return null query failed");
return;
}
JsonData jsonData = JsonMapper.ToObject(ret);
JsonData codeData = jsonData["code"];
int code = codeData != null ? (int)codeData : -1;
string msg = jsonData["msg"].ToString();
TraceLog.Trace("HeroSentimentSvc.OnReportBanLogData ret {0} code {1} msg {2}", ret, code, msg);
if (code != 0)
{
TraceLog.Error("HeroSentimentSvc.OnReportBanLogData: ret code : {0} msg :{1}", code, msg);
return;
}
}
}
}