Jump to content
metin2dev
  • 0
Sanchez

[C++]Advanced chat & spam protection

Question

Hi,
In this thread I will show you how you can implement an advanced spam protection to your server.

  • The player can write the same message just after 5 seconds.
  • The player will receive 1 minute of chat ban if he is trying to send the same message more than 3 times in 5 seconds.
  • You can specify a blockspamlist.lst file which contains words. If the user sending a message which contains a words from the list, the player will receive 5 minutes of chat ban. (You can specify the time of the chat ban)
  • You can specify a bannspamlist.lst file which contains words. If the user sending a message which contains a words from the list, the player will receive a ban. (You can specify the time of the ban)

Open game/input_main.cpp and search for this: 

if (ch->IncreaseChatCounter() >= 10)

Add this over that:

	if (!strcmp(ch->LastPlayerMessage, buf) && (thecore_pulse() < (ch->LastMessageAt + SPAM_WAIT_SEC * 25)) && !ch->SpamAllowBuf(buf) && ch->GetGMLevel() < GM_LOW_WIZARD)
	{
		if (ch->BlockChatAfter < 2)
		{
			ch->ChatPacket(CHAT_TYPE_INFO, ("You must wait 5 seconds to repeat your message"));
			ch->BlockChatAfter++;
			return iExtraLen;
		}
		else
		{
			ch->BlockChatAfter = 0;
			ch->PlayerPunish(false, SPAM_CHAT_BAN_TIME);
			return iExtraLen;
		}
	}
	else
	{
		if (!ch->BannListCheck(buf) && ch->GetGMLevel() < GM_LOW_WIZARD)
		{
			ch->PlayerPunish(true, SPAM_BAN_TIME);
			return iExtraLen;
		}
		if (!ch->SpamListCheck(buf) && ch->GetGMLevel() < GM_LOW_WIZARD)
		{
			ch->ChatPacket(CHAT_TYPE_INFO, ("You wrote a not allowed words!"));
			ch->PlayerPunish(false, SPAM_CHAT_BAN_TIME);
			return iExtraLen;
		}
	}

Still in input_main.cpp search for this:

ch->GetMapIndex(), strlen(ch->GetName())));

Add this under that:

			strcpy(ch->LastPlayerMessage, buf);
			ch->LastMessageAt = thecore_pulse();
			ch->BlockChatAfter = 0;

Open game/char.h and search for this: 

BYTE			GetChatCounter() const;

Add this under that:

		int				LastMessageAt;
		int				BlockChatAfter;
		char			LastPlayerMessage[CHAT_MAX_LEN + 1];
		void			PlayerPunish(bool PowerPunish, int Duration);
		bool			SpamListCheck(const char *Message);
		bool			BannListCheck(const char *Message);
		bool			SpamAllowBuf(const char *Message);

Open game/char.cpp and add these events:

void CHARACTER::PlayerPunish(bool PowerPunish, int Duration)
{
	if (!PowerPunish)
	{
		AddAffect(AFFECT_BLOCK_CHAT, POINT_NONE, 0, AFF_NONE, Duration, 0, true);
		sys_log(0, "%s[%d] has been chatbanned because of spamming/writing words which are in the spamlist.txt", GetName(), GetPlayerID());
	}
	else
	{
		std::auto_ptr<SQLMsg> msg(DBManager::instance().DirectQuery("UPDATE account.account SET availDt = FROM_UNIXTIME(UNIX_TIMESTAMP(CURRENT_TIMESTAMP()) + %i) WHERE id = %d", Duration, GetAID()));
		sys_log(0, "%s[%d] has been banned because of saying blacklisted word", GetName(), GetPlayerID());
		GetDesc()->DelayedDisconnect(5);
	}
}

bool CHARACTER::SpamAllowBuf(const char *Message)
{
	if (!strcmp(Message, "(Ȳ´ç)") || !strcmp(Message, "(µ·)") || !strcmp(Message, "(±â»Ý)") || !strcmp(Message, "(ÁÁľĆ)") || !strcmp(Message, "(»ç¶ű)") || !strcmp(Message, "(şĐłë)") || !strcmp(Message, "(ľĆÇĎ)") || !strcmp(Message, "(żěżď)") || !strcmp(Message, "(ÁËĽŰ)"))
	{
		return true;
	}

	return false;
}

bool CHARACTER::SpamListCheck(const char *Message)
{
	for (int i = 0; i < SpamBlockListArray.size(); i++)
	{
		if (!strcmp(Message, SpamBlockListArray[i].c_str()))
		{
			return false;
		}
	}

	return true;
}

bool CHARACTER::BannListCheck(const char *Message)
{
	for (int i = 0; i < SpamBannListArray.size(); i++)
	{
		if (!strcmp(Message, SpamBannListArray[i].c_str()))
		{
			return false;
		}
	}

	return true;
}

Add 2 new files to your project:

  • spamblock.cpp
  • spamblock.h

Add these to the spamblock.cpp

#include "fstream"
#include "string"
#include "sstream"
#include "stdafx.h"
#include "../../common/length.h"


std::vector<std::string> SpamBlockListArray;
std::vector<std::string> SpamBannListArray;

void LoadBlockSpamList()
{
	std::string TempBlockList;
	std::ifstream File("chat/blockspamlist.lst");

	if (!File.is_open())
	{
		sys_log(0, "WARNING: cannot open chat/blockspamlist.lst");
		return;
	}

	SpamBlockListArray.clear();

	while (!File.eof())
	{
		File >> TempBlockList;
		SpamBlockListArray.push_back(TempBlockList);
	}

	File.close();
}

void LoadBannSpamList()
{
	std::string TempBannList;
	std::ifstream File("chat/bannspamlist.lst");

	if (!File.is_open())
	{
		sys_log(0, "WARNING: cannot open chat/bannspamlist.lst");
		return;
	}

	SpamBannListArray.clear();

	while (!File.eof())
	{
		File >> TempBannList;
		SpamBannListArray.push_back(TempBannList);
	}

	File.close();
}

Add these to the spamblock.h

#include "string"
#include "../../common/length.h"


extern void LoadBlockSpamList();
extern void LoadBannSpamList();
extern std::vector<std::string> SpamBlockListArray;
extern std::vector<std::string> SpamBannListArray;

Add this to game/char.cpp

#include "spamblock.h"

Add this to game/main.cpp

#include "spamblock.h"

Search for this in game/main.cpp:

PanamaLoad();

Add these under that:

	LoadBlockSpamList();
	LoadBannSpamList();

Open common/length.h and add these:

SPAM_WAIT_SEC           = 5, // The player can duplicate his message after 5 sec
SPAM_CHAT_BAN_TIME      = 60, // The player will receive 60 seconds chat ban, if he is saying a spamlist word
SPAM_BAN_TIME           = 3600, // The player will receive 1 hour ban, if he is saying a banlist word

Open game/cmd_gm.cpp and search for this event:

ACMD(do_reload)

Add this to the switch function:

			case 'b':
				ch->ChatPacket(CHAT_TYPE_INFO, "Reloading bann/spam list infomations.");
				LoadBlockSpamList();
				LoadBannSpamList();
				sys_log(0, "Reloading bann/spam list infomations.");
				break;

Add this to the top of the file:

#include "spamblock.h"

How to set up:

  • Make sure you added everything to your game
  • Create a new folder called chat in your channels
  • Create 2 files, blockspamlist.lst and bannspamlist.lst
  • Upload the words to these files
  • Restart your server

blockspamlist.lst example:

obsceneword1
obsceneword2
obsceneword3
obsceneword4

bannspamlist.lst example:

WWW.CHEAP-FARM-SERVICE.COM
WWW.CHEAP-GOLD.COM
WWW.EASY-HACKS.COM
WWW.FREE-YANG.COM

 
If you have any question or suggestion, please just reply to this topic.
 
Kind Regards,
Sanchez

  • Like 43

Share this post


Link to post
Share on other sites

Recommended Posts

  • 0

Hmm, really nice.

One idea is to:
Extend the gm command by name reload to reloading the text files :D
for example: /reload bann or something else if it is possible

 

Sorry for my bad english, I hope you mean it.

 

Magyarul:

Bővítsd ki a /reload parancsot, hogy ne kelljen újraindítani a szervert ha frissülnek a szöveges fájlok.

  • Like 1

Share this post


Link to post
Share on other sites
  • 0

Hi guys,

 

The first post has been updated with these changes:

  1. You don't need to specify the length of the files anymore
  2. GMs can spam and write blacklisted words
  3. With the /reload b command you can reload the block and ban list, so you don't need to restart the server if you are added new words
  • Like 1

Share this post


Link to post
Share on other sites
  • 0

Sanchez, today I tried to your system in my core development.

 

But there is a little bug. 5 Second spam rule does not work, except this block spam & ban word works as well.

	if (buflen > 1 && *buf == '/')
	{
		interpret_command(ch, buf + 1, buflen - 1);
		return iExtraLen;
	}

	if (!strcmp(ch->LastPlayerMessage, buf) && (thecore_pulse() < (ch->LastMessageAt + SPAM_WAIT_SEC * 25)) && !ch->SpamAllowBuf(buf) && ch->GetGMLevel() < GM_LOW_WIZARD)
	{
		if (ch->BlockChatAfter < 2)
		{				
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You must wait 5 seconds to repeat your message."));
			
			ch->BlockChatAfter++;
			return iExtraLen;
		}
		else
		{
			ch->BlockChatAfter = 0;
			ch->PlayerPunish(false, SPAM_CHAT_BAN_TIME);
			return iExtraLen;
		}
	}
	else
	{
		if (!ch->BannListCheck(buf) && ch->GetGMLevel() < GM_LOW_WIZARD)
		{
			ch->PlayerPunish(true, SPAM_BAN_TIME);
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You has been banned %d minute because of saying blacklisted word."), SPAM_BAN_TIME);

			return iExtraLen;
		}
		if (!ch->SpamListCheck(buf) && ch->GetGMLevel() < GM_LOW_WIZARD)
		{				
			ch->PlayerPunish(false, SPAM_CHAT_BAN_TIME);
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your chat has been banned %d minute because of saying spamming word."), SPAM_CHAT_BAN_TIME);
			
			return iExtraLen;
		}
	}

	if (ch->IncreaseChatCounter() >= 10)
	{
		if (ch->GetChatCounter() == 10)
		{
			sys_log(0, "CHAT_HACK: %s", ch->GetName());
			ch->GetDesc()->DelayedDisconnect(5);
		}

		return iExtraLen;
	}

Interesting?

  • Like 1

Share this post


Link to post
Share on other sites
  • 0

Sanchez, today I tried to your system in my core development.

 

But there is a little bug. 5 Second spam rule does not work, except this block spam & ban word works as well.

Interesting?

Maybe because of in the last update I made a GM check.

 

ch->GetGMLevel() < GM_LOW_WIZARD

If your GM level is bigger than GM_LOW_WIZARD then you can spam and write blacklisted words.

Share this post


Link to post
Share on other sites
  • 0

Interesting, it works properly for me. Maybe check it with writing the line of the actual running code with this:

sys_log(0, "%d", __LINE__);

Share this post


Link to post
Share on other sites
  • 0

I'm using the mainline, but I don't think there is "chat" different between these lines. If you give me Teamviewer I can check it for you.

  • Like 1

Share this post


Link to post
Share on other sites
  • 0

Hello everybody,

 

i would like to ask you to one thing.

 

You must create a new enum? For Spam system? if so how am I supposed to write
 

Open common/length.h and add these:

1
2
3
SPAM_WAIT_SEC           = 5, // The player can duplicate his message after 5 sec
SPAM_CHAT_BAN_TIME      = 60, // The player will receive 60 seconds chat ban, if he is saying a spamlist word
SPAM_BAN_TIME           = 3600, // The player will receive 1 hour ban, if he is saying a banlist word

 

 

Thank you a lot

 

Dumik

Share this post


Link to post
Share on other sites
  • 0

Hello everybody,

 

i have another problem. I add all codes. Compilation was successful but when i login as GM the client kick me out after loading window.

 

in syserror is this:

0401 19:45:40573 :: Unknown packet header: 148, last: 45 81

Do you know how to fix it? 

 

Thank you a lot

 

Best regards,

 

Vojta

Share this post


Link to post
Share on other sites
  • 0

I have this error:

 

 
linking ../game_r40999....
.obj/char.o: In function `std::vector<std::string, std::allocator<std::string> >::size() const':
/usr/include/c++/4.2/bits/stl_vector.h:408: undefined reference to `SpamBlockListArray'
/usr/include/c++/4.2/bits/stl_vector.h:408: undefined reference to `SpamBlockListArray'
/usr/include/c++/4.2/bits/stl_vector.h:408: undefined reference to `SpamBannListArray'
/usr/include/c++/4.2/bits/stl_vector.h:408: undefined reference to `SpamBannListArray'
.obj/main.o: In function `main':
/usr/xavi/source-server/Srcs/Server/game/src/main.cpp:512: undefined reference to `LoadBlockSpamList()'
/usr/xavi/source-server/Srcs/Server/game/src/main.cpp:513: undefined reference to `LoadBannSpamList()'
gmake: *** [../game_r40999] Error 1
 

Share this post


Link to post
Share on other sites
  • 0
if (!strcmp(ch->LastPlayerMessage, buf) && (thecore_pulse() < (ch->LastMessageAt + SPAM_WAIT_SEC * 25)) && !ch->SpamAllowBuf(buf) 

You shouldn't hardcode the passes per second as 25, some people might change it... so you should replace 

* 25

 with 

* passes_per_sec
  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×