[Rules] Add rule to allow players to permanently save chat channels to database, up to a limit. (#2706)

* Initial code

* Tweak

* Rule description tweak

* More channel work

* More adjustments

* Auto-join saved permanent player channels

* Fix UCS crash if player has no channels to load from table.

* Implemented channel blocking feature

* Update database when player channel's owner or password change

* First round of requested changes.

* Logic tweak to ensure player channels are sets to permanent when appropraite

* name_filter table integration and some refactoring

* Use new `reserved_channel_names` table to block specific channel names.

* Remove some legacy channel block code

* Setup required SQL update to create  `reserved_channel_names`  table.

* Update db_update_manifest.txt

* Update db_update_manifest.txt

* Update chatchannel.cpp

* Code review

* Database to UCSDatabase

* Repository SaveChatChannel

* CurrentPlayerChannelCount repository

* Cleanup name filter

* CreateChannel

* Update websocketpp

* Increment CURRENT_BINARY_DATABASE_VERSION

Set to 9216

* Minor tweaks to blocked channel name checks & other related areas.

- Enforce blocked channel names on channel creation.
- Also enforce blocked channel names on channel join.
- Add channel status check to Debug logging.
- Minor formatting adjustments.
- Add single quotes to column name value in query.

* Minor log change

* Increment DB Version

* Formatting Tweaks

- Made formatting adjustments consistent with KinglyKrab's recommended changes.
- This compiles successfully with these changes, but unable to test the changes until this weekend.

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Vayle
2023-01-18 23:42:09 -05:00
committed by GitHub
parent 03a27b02ff
commit 29473aa7f5
16 changed files with 1409 additions and 286 deletions
+140 -28
View File
@@ -49,6 +49,9 @@
#include "../common/misc_functions.h"
#include "../common/strings.h"
#include "chatchannel.h"
#include "../common/repositories/chatchannel_reserved_names_repository.h"
#include "../common/repositories/chatchannels_repository.h"
#include "../common/repositories/name_filter_repository.h"
extern Clientlist *g_Clientlist;
extern std::string GetMailPrefix();
@@ -219,51 +222,160 @@ bool UCSDatabase::GetVariable(const char *varname, char *varvalue, uint16 varval
return true;
}
bool UCSDatabase::LoadChatChannels()
{
LoadReservedNamesFromDB();
LogInfo("Loading chat channels from the database");
const std::string query = "SELECT `name`, `owner`, `password`, `minstatus` FROM `chatchannels`";
auto results = QueryDatabase(query);
auto results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
std::string channelName = row[0];
std::string channelOwner = row[1];
std::string channelPassword = row[2];
std::string channel_name = row[0];
std::string channel_owner = row[1];
std::string channel_password = row[2];
auto channel_min_status = row[3];
ChannelList->CreateChannel(channelName, channelOwner, channelPassword, true, atoi(row[3]));
if (!ChannelList->FindChannel(channel_name)) {
ChannelList->CreateChannel(channel_name, channel_owner, channel_password, true, atoi(channel_min_status), false);
}
}
return true;
}
void UCSDatabase::SetChannelPassword(std::string channelName, std::string password)
void UCSDatabase::LoadReservedNamesFromDB()
{
LogInfo("UCSDatabase::SetChannelPassword([{}], [{}])", channelName.c_str(), password.c_str());
ChatChannelList::ClearChannelBlockList();
std::string query = StringFormat(
"UPDATE `chatchannels` SET `password` = '%s' WHERE `name` = '%s'",
password.c_str(), channelName.c_str());
auto channels = ChatchannelReservedNamesRepository::All(*this);
if (channels.empty()) {
LogDebug("No reserved names exist in the database...");
}
for (auto &e: channels) {
ChatChannelList::AddToChannelBlockList(e.name);
LogInfo("Adding channel [{}] to blocked list from database...", e.name);
}
LogInfo("Loaded [{}] reserved channel name(s)", channels.size());
}
bool UCSDatabase::IsChatChannelInDB(const std::string& channel_name)
{
auto r = ChatchannelsRepository::Count(
*this,
fmt::format(
"name = {}", Strings::Escape(channel_name)
)
);
return r > 0;
}
void UCSDatabase::SaveChatChannel(
const std::string& channel_name,
const std::string& channel_owner,
const std::string& channel_password,
const uint16& min_status
)
{
auto e = ChatchannelsRepository::GetWhere(
*this,
fmt::format(
"`name` = '{}' LIMIT 1", Strings::Escape(channel_name)
)
);
// If channel name is blocked, do not save it to the database
if (ChatChannelList::IsOnChannelBlockList(channel_name)) {
LogInfo("Channel [{}] already found on the block list, ignoring", channel_name);
return;
}
// update if exists, create new if it doesn't
auto c = !e.empty() ? e[0] : ChatchannelsRepository::NewEntity();
c.name = channel_name;
c.owner = channel_owner;
c.password = channel_password;
c.minstatus = min_status;
if (e.empty()) {
ChatchannelsRepository::InsertOne(*this, c);
return;
}
ChatchannelsRepository::UpdateOne(*this, c);
}
void UCSDatabase::DeleteChatChannel(const std::string& channel_name)
{
ChatchannelsRepository::DeleteWhere(*this, fmt::format("`name` = '{}'", Strings::Escape(channel_name)));
LogInfo("Deleting channel [{}] from the database.", channel_name);
}
std::string UCSDatabase::CurrentPlayerChannels(const std::string& player_name) {
int current_player_channel_count = CurrentPlayerChannelCount(player_name);
if (current_player_channel_count == 0) {
return "";
}
const auto rquery = fmt::format("SELECT GROUP_CONCAT(`name` SEPARATOR ', ') FROM chatchannels WHERE `owner` = '{}'; ", Strings::Escape(player_name));
auto results = QueryDatabase(rquery);
auto row = results.begin();
std::string channels = row[0];
LogDebug("Player [{}] has the following permanent channels saved to the database: [{}].", player_name, channels);
return channels;
}
int UCSDatabase::CurrentPlayerChannelCount(const std::string& player_name)
{
return (int) ChatchannelsRepository::Count(*this, fmt::format("`owner` = '{}'", Strings::Escape(player_name)));
}
void UCSDatabase::SetChannelPassword(const std::string& channel_name, const std::string& password)
{
LogInfo("UCSDatabase::SetChannelPassword([{}], [{}])", channel_name.c_str(), password.c_str());
std::string query = fmt::format(
"UPDATE `chatchannels` SET `password` = '{}' WHERE `name` = '{}'",
Strings::Escape(password), Strings::Escape(channel_name));
QueryDatabase(query);
}
void UCSDatabase::SetChannelOwner(std::string channelName, std::string owner)
void UCSDatabase::SetChannelOwner(const std::string& channel_name, const std::string& owner)
{
LogInfo("UCSDatabase::SetChannelOwner([{}], [{}])", channelName.c_str(), owner.c_str());
LogInfo("Setting channel [{}] owner to [{}]", channel_name, owner);
std::string query = StringFormat(
"UPDATE `chatchannels` SET `owner` = '%s' WHERE `name` = '%s'",
owner.c_str(),
channelName.c_str()
std::string query = fmt::format(
"UPDATE `chatchannels` SET `owner` = '{}' WHERE `name` = '{}'",
Strings::Escape(owner),
Strings::Escape(channel_name)
);
QueryDatabase(query);
}
bool UCSDatabase::CheckChannelNameFilter(const std::string& channel_name)
{
LogDebug("Checking if [{}] is on the name filter", channel_name);
// TODO: This should potentially just be pulled into memory at some other point
// This if fine for now
for (auto &e: NameFilterRepository::All(*this)) {
if (Strings::Contains(Strings::ToLower(channel_name), Strings::ToLower(e.name))) {
LogInfo("Failed to pass name filter check for [{}] against word [{}]", channel_name, e.name);
return false;
}
}
LogDebug("Name Filter Check Passed!");
return true;
}
void UCSDatabase::SendHeaders(Client *client)
{
@@ -356,7 +468,7 @@ void UCSDatabase::SendHeaders(Client *client)
}
void UCSDatabase::SendBody(Client *client, int messageNumber)
void UCSDatabase::SendBody(Client *client, const int& messageNumber)
{
int characterID = FindCharacter(client->MailBoxName().c_str());
@@ -412,11 +524,11 @@ void UCSDatabase::SendBody(Client *client, int messageNumber)
}
bool UCSDatabase::SendMail(
std::string recipient,
std::string from,
std::string subject,
std::string body,
std::string recipientsString
const std::string& recipient,
const std::string& from,
const std::string& subject,
const std::string& body,
const std::string& recipientsString
)
{
@@ -484,7 +596,7 @@ bool UCSDatabase::SendMail(
return true;
}
void UCSDatabase::SetMessageStatus(int messageNumber, int status)
void UCSDatabase::SetMessageStatus(const int& messageNumber, const int& status)
{
LogInfo("SetMessageStatus [{}] [{}]", messageNumber, status);
@@ -557,7 +669,7 @@ void UCSDatabase::ExpireMail()
}
}
void UCSDatabase::AddFriendOrIgnore(int charID, int type, std::string name)
void UCSDatabase::AddFriendOrIgnore(const int& charID, const int& type, const std::string& name)
{
std::string query = StringFormat(
"INSERT INTO `friends` (`charid`, `type`, `name`) "
@@ -579,7 +691,7 @@ void UCSDatabase::AddFriendOrIgnore(int charID, int type, std::string name)
}
}
void UCSDatabase::RemoveFriendOrIgnore(int charID, int type, std::string name)
void UCSDatabase::RemoveFriendOrIgnore(const int& charID, const int& type, const std::string& name)
{
std::string query = StringFormat(
"DELETE FROM `friends` WHERE `charid` = %i AND `type` = %i AND `name` = '%s'",
@@ -600,7 +712,7 @@ void UCSDatabase::RemoveFriendOrIgnore(int charID, int type, std::string name)
}
}
void UCSDatabase::GetFriendsAndIgnore(int charID, std::vector<std::string> &friends, std::vector<std::string> &ignorees)
void UCSDatabase::GetFriendsAndIgnore(const int& charID, std::vector<std::string> &friends, std::vector<std::string> &ignorees)
{
std::string query = StringFormat("select `type`, `name` FROM `friends` WHERE `charid`=%i", charID);