it works, now to clean up more

This commit is contained in:
Aaron Kimbre 2025-01-31 20:22:42 -06:00
parent 6978b56016
commit 5839a888bb
9 changed files with 93 additions and 36 deletions

View File

@ -105,7 +105,7 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowLis
} }
} }
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) { std::set<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true. if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
if (message.empty()) return { }; if (message.empty()) return { };
if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } }; if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
@ -114,7 +114,7 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
std::string segment; std::string segment;
std::regex reg("(!*|\\?*|\\;*|\\.*|\\,*)"); std::regex reg("(!*|\\?*|\\;*|\\.*|\\,*)");
std::vector<std::pair<uint8_t, uint8_t>> listOfBadSegments = std::vector<std::pair<uint8_t, uint8_t>>(); std::set<std::pair<uint8_t, uint8_t>> listOfBadSegments = std::set<std::pair<uint8_t, uint8_t>>();
uint32_t position = 0; uint32_t position = 0;
@ -127,17 +127,17 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
size_t hash = CalculateHash(segment); size_t hash = CalculateHash(segment);
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) { if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace(position, originalSegment.length());
} }
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) { if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
m_UserUnapprovedWordCache.push_back(hash); m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace(position, originalSegment.length());
} }
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) { if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
m_UserUnapprovedWordCache.push_back(hash); m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace(position, originalSegment.length());
} }
position += originalSegment.length() + 1; position += originalSegment.length() + 1;

View File

@ -24,7 +24,7 @@ public:
void ReadWordlistPlaintext(const std::string& filepath, bool allowList); void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
bool ReadWordlistDCF(const std::string& filepath, bool allowList); bool ReadWordlistDCF(const std::string& filepath, bool allowList);
void ExportWordlistToDCF(const std::string& filepath, bool allowList); void ExportWordlistToDCF(const std::string& filepath, bool allowList);
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true); std::set<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
private: private:
bool m_DontGenerateDCF; bool m_DontGenerateDCF;

View File

@ -11,7 +11,7 @@ target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
add_library(dChatServer ${DCHATSERVER_SOURCES}) add_library(dChatServer ${DCHATSERVER_SOURCES})
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer") target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer" "${PROJECT_SOURCE_DIR}/dChatFilter")
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer dWeb) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer dWeb)

View File

@ -436,21 +436,20 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
data.sender = Game::playerContainer.GetPlayerData(sender); data.sender = Game::playerContainer.GetPlayerData(sender);
if (!data.sender || data.sender.GetIsMuted()) return; if (!data.sender || data.sender.GetIsMuted()) return;
eChatChannel channel;
uint32_t size; uint32_t size;
inStream.IgnoreBytes(4); inStream.IgnoreBytes(4);
inStream.Read(data.channel); inStream.Read(data.channel);
inStream.Read(size); inStream.Read(size);
inStream.IgnoreBytes(77); inStream.IgnoreBytes(77);
LOG("message size: %u", size);
data.message = LUWString(size); data.message = LUWString(size);
inStream.Read(data.message); inStream.Read(data.message);
LOG("Got message from (%s) via [%s]: %s", data.sender.playerName.c_str(), StringifiedEnum::ToString(data.channel).data(), data.message.GetAsString().c_str()); LOG("Got message from (%s) via [%s]: %s", data.sender.playerName.c_str(), StringifiedEnum::ToString(data.channel).data(), data.message.GetAsString().c_str());
switch (channel) { switch (data.channel) {
case eChatChannel::LOCAL: { case eChatChannel::LOCAL: {
break; break;
} }
@ -467,7 +466,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
break; break;
} }
default: default:
LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(channel).data()); LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(data.channel).data());
break; break;
} }
ChatWeb::SendWSChatMessage(data); ChatWeb::SendWSChatMessage(data);
@ -526,7 +525,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
SendPrivateChatMessage(data.sender, data.receiver, data.sender, data.message, data.channel, eChatMessageResponseCode::SENT); SendPrivateChatMessage(data.sender, data.receiver, data.sender, data.message, data.channel, eChatMessageResponseCode::SENT);
// To the receiver: // To the receiver:
SendPrivateChatMessage(data.sender, data.receiver, data.receiver, data.message, data.channel, eChatMessageResponseCode::RECEIVEDNEWWHISPER); SendPrivateChatMessage(data.sender, data.receiver, data.receiver, data.message, data.channel, eChatMessageResponseCode::RECEIVEDNEWWHISPER);
// To the WebSocket // To the websocket:
ChatWeb::SendWSChatMessage(data); ChatWeb::SendWSChatMessage(data);
return; return;
} }

View File

@ -2,8 +2,7 @@
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dNetCommon.h" #include "dNetCommon.h"
#include "BitStream.h" #include "BitStream.h"
#include "PlayerContainer.h"
struct PlayerData;
enum class eAddFriendResponseType : uint8_t; enum class eAddFriendResponseType : uint8_t;

View File

@ -16,6 +16,8 @@
#include "Database.h" #include "Database.h"
#include "ChatJSONUtils.h" #include "ChatJSONUtils.h"
#include "JSONUtils.h" #include "JSONUtils.h"
#include "eGameMasterLevel.h"
#include "dChatFilter.h"
using json = nlohmann::json; using json = nlohmann::json;
@ -57,15 +59,53 @@ void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
} }
void HandleWSChat(mg_connection* connection, json data) { void HandleWSChat(mg_connection* connection, json data) {
auto check = JSONUtils::CheckRequiredData(data, { "user", "message" }); auto check = JSONUtils::CheckRequiredData(data, { "user", "message", "gmlevel", "zone" });
if (!check.empty()) { if (!check.empty()) {
LOG_DEBUG("Received invalid websocket message: %s", check.c_str()); LOG_DEBUG("Received invalid websocket message: %s", check.c_str());
} else { } else {
const auto user = data["user"].get<std::string>(); const auto user = data["user"].get<std::string>();
const auto message = data["message"].get<std::string>(); const auto message = data["message"].get<std::string>();
LOG_DEBUG("EXTERNAL Chat message from %s: %s", user.c_str(), message.c_str()); const auto gmlevel = GeneralUtils::TryParse<eGameMasterLevel>(data["gmlevel"].get<std::string>()).value_or(eGameMasterLevel::CIVILIAN);
// TODO: use chat filter and respond if the message isn't allowed const auto zone = data["zone"].get<uint32_t>();
// TODO: Send chat message to corret world server to broadcast to players
const auto filter_check = Game::chatFilter->IsSentenceOkay(message, gmlevel);
if (!filter_check.empty()) {
LOG_DEBUG("Chat message \"%s\" from %s was not allowed", message.c_str(), user.c_str());
data["error"] = "Chat message blocked by filter";
data["filtered"] = json::array();
for (const auto& filtered : filter_check) {
data["filtered"].push_back(message.substr(filtered.first, filtered.second));
}
mg_ws_send(connection, data.dump().c_str(), data.dump().size(), WEBSOCKET_OP_TEXT);
return;
}
LOG("%s: %s", user.c_str(), message.c_str());
// bodge to test
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE);
bitStream.Write(zone);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE);
bitStream.Write<uint64_t>(0);
bitStream.Write(eChatChannel::LOCAL);
bitStream.Write<uint32_t>(message.size());
bitStream.Write(LUWString(user));
bitStream.Write<uint64_t>(0);
bitStream.Write<uint16_t>(0);
bitStream.Write<char>(0);
for (uint32_t i = 0; i < message.size(); ++i) {
bitStream.Write<uint16_t>(message[i]);
}
bitStream.Write<uint16_t>(0);
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
// send to world servers with macthing zone
// Cry: this is the hard part since there is no instance manager
// Do we send it to master and let master sort it out via instance manager?
} }
} }
@ -110,7 +150,7 @@ namespace ChatWeb {
json data; json data;
data["player_data"] = player; data["player_data"] = player;
data["update_type"] = magic_enum::enum_name(activityType); data["update_type"] = magic_enum::enum_name(activityType);
Game::web.SendWSMessage("player_update", data); Game::web.SendWSMessage("player", data);
} }
void SendWSChatMessage(const ChatMessage& chatMessage) { void SendWSChatMessage(const ChatMessage& chatMessage) {

View File

@ -134,7 +134,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t rep
LOG("Sent CreateCharacter for ID: %llu", player); LOG("Sent CreateCharacter for ID: %llu", player);
} }
void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector<std::pair<uint8_t, uint8_t>> unacceptedItems) { void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set<std::pair<uint8_t, uint8_t>> unacceptedItems) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHAT_MODERATION_STRING); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHAT_MODERATION_STRING);

View File

@ -32,7 +32,7 @@ namespace WorldPackets {
void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift);
void SendServerState(const SystemAddress& sysAddr); void SendServerState(const SystemAddress& sysAddr);
void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm);
void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector<std::pair<uint8_t, uint8_t>> unacceptedItems); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set<std::pair<uint8_t, uint8_t>> unacceptedItems);
void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel);
void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info); void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info);
void SendDebugOuput(const SystemAddress& sysAddr, const std::string& data); void SendDebugOuput(const SystemAddress& sysAddr, const std::string& data);

View File

@ -647,6 +647,21 @@ void HandlePacketChat(Packet* packet) {
break; break;
} }
case MessageType::Chat::GENERAL_CHAT_MESSAGE: {
// First get the zone and check if we should forward it
CINSTREAM_SKIP_HEADER;
uint32_t zoneID;
inStream.Read(zoneID);
if (zoneID != Game::server->GetZoneID()) return;
//Write our stream outwards:
CBITSTREAM;
unsigned char data;
while (inStream.Read(data)) {
bitStream.Write(data);
}
SEND_PACKET_BROADCAST;
break;
}
default: default:
LOG("Received an unknown chat: %i", int(packet->data[3])); LOG("Received an unknown chat: %i", int(packet->data[3]));
} }
@ -1288,7 +1303,7 @@ void HandlePacket(Packet* packet) {
} }
} }
std::vector<std::pair<uint8_t, uint8_t>> segments = Game::chatFilter->IsSentenceOkay(request.message, entity->GetGMLevel(), !(isBestFriend && request.chatLevel == 1)); auto segments = Game::chatFilter->IsSentenceOkay(request.message, entity->GetGMLevel(), !(isBestFriend && request.chatLevel == 1));
bool bAllClean = segments.empty(); bool bAllClean = segments.empty();
@ -1327,25 +1342,29 @@ void HandlePacket(Packet* packet) {
std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message); std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message);
LOG("%s: %s", playerName.c_str(), sMessage.c_str()); LOG("%s: %s", playerName.c_str(), sMessage.c_str());
ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message); ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message);
{
// TODO: make it so we don't write this manually, but instead use a proper read and writes
// aka: this is awful and should be fixed, but I can't be bothered to do it right now
// Forward to the chat server
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE);
CBITSTREAM; bitStream.Write(user->GetLoggedInChar());
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE); bitStream.Write<uint32_t>(chatMessage.message.size());
bitStream.Write(chatMessage.chatChannel);
bitStream.Write<uint32_t>(chatMessage.message.size());
bitStream.Write(user->GetLoggedInChar()); for (uint32_t i = 0; i < 77; ++i) {
bitStream.Write<uint32_t>(chatMessage.message.size()); bitStream.Write<uint8_t>(0);
bitStream.Write(chatMessage.chatChannel); }
bitStream.Write<uint32_t>(chatMessage.message.size());
for (uint32_t i = 0; i < 77; ++i) { for (uint32_t i = 0; i < chatMessage.message.size(); ++i) {
bitStream.Write<uint8_t>(0); bitStream.Write<uint16_t>(chatMessage.message[i]);
}
bitStream.Write<uint16_t>(0);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false);
} }
for (uint32_t i = 0; i < chatMessage.message.size(); ++i) {
bitStream.Write<uint16_t>(chatMessage.message[i]);
}
bitStream.Write<uint16_t>(0);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false);
} }
break; break;