core/ChatTriggers.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 2042 4a2faafca477
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
ds@1952
     1
/**
ds@1952
     2
 * vim: set ts=4 :
ds@1952
     3
 * =============================================================================
ds@1952
     4
 * SourceMod
ds@1952
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1952
     6
 * =============================================================================
ds@1952
     7
 *
ds@1952
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1952
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1952
    10
 * Free Software Foundation.
ds@1952
    11
 * 
ds@1952
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1952
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1952
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1952
    15
 * details.
ds@1952
    16
 *
ds@1952
    17
 * You should have received a copy of the GNU General Public License along with
ds@1952
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
ds@1952
    19
 *
ds@1952
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1952
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1952
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1952
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1952
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1952
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1952
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1952
    27
 * or <http://www.sourcemod.net/license.php>.
ds@1952
    28
 *
ds@1952
    29
 * Version: $Id$
ds@1952
    30
 */
ds@1952
    31
ds@1952
    32
#include <ITextParsers.h>
ds@1952
    33
#include "ChatTriggers.h"
ds@1952
    34
#include "sm_stringutil.h"
ds@1952
    35
#include "ConCmdManager.h"
ds@1952
    36
#include "PlayerManager.h"
ds@1952
    37
#include "Translator.h"
ds@1952
    38
#include "HalfLife2.h"
ds@1952
    39
ds@1952
    40
/* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does.
ds@1952
    41
 * While the OB build only runs on MM:S 1.6.0+ (SH 5+), the older one 
ds@1952
    42
 * can technically be compiled against any MM:S version after 1.4.2.
ds@1952
    43
 */
ds@1952
    44
#if defined ORANGEBOX_BUILD
ds@1952
    45
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate1<const CCommand &, void>);
ds@1952
    46
extern int __SourceHook_FHAddConCommandDispatch(void *, ISourceHook::AddHookMode, bool, class fastdelegate::FastDelegate1<const CCommand &, void>);
ds@1952
    47
#else
ds@1952
    48
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
ds@1952
    49
#if SH_IMPL_VERSION >= 5
ds@1952
    50
extern int __SourceHook_FHAddConCommandDispatch(void *, ISourceHook::AddHookMode, bool, class fastdelegate::FastDelegate0<void>);
ds@1952
    51
#elif SH_IMPL_VERSION == 4
ds@1952
    52
extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
ds@1952
    53
#elif SH_IMPL_VERSION == 3
ds@1952
    54
extern bool __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
ds@1952
    55
#endif //SH_IMPL_VERSION
ds@1952
    56
#endif //ORANGEBOX_BUILD
ds@1952
    57
ds@1952
    58
ChatTriggers g_ChatTriggers;
ds@1952
    59
bool g_bSupressSilentFails = false;
ds@1952
    60
CPhraseFile *g_pFloodPhrases = NULL;
ds@1952
    61
ds@1952
    62
ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false), 
ds@1952
    63
	m_bTriggerWasSilent(false), m_ReplyTo(SM_REPLY_CONSOLE)
ds@1952
    64
{
ds@1952
    65
	m_PubTrigger = sm_strdup("!");
ds@1952
    66
	m_PrivTrigger = sm_strdup("/");
ds@1952
    67
	m_PubTriggerSize = 1;
ds@1952
    68
	m_PrivTriggerSize = 1;
ds@1952
    69
	m_bIsChatTrigger = false;
ds@1952
    70
}
ds@1952
    71
ds@1952
    72
ChatTriggers::~ChatTriggers()
ds@1952
    73
{
ds@1952
    74
	delete [] m_PubTrigger;
ds@1952
    75
	m_PubTrigger = NULL;
ds@1952
    76
	delete [] m_PrivTrigger;
ds@1952
    77
	m_PrivTrigger = NULL;
ds@1952
    78
}
ds@1952
    79
ds@1952
    80
ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key, 
ds@1952
    81
													const char *value, 
ds@1952
    82
													ConfigSource source,
ds@1952
    83
													char *error, 
ds@1952
    84
													size_t maxlength)
ds@1952
    85
{
ds@1952
    86
	if (strcmp(key, "PublicChatTrigger") == 0)
ds@1952
    87
	{
ds@1952
    88
		delete [] m_PubTrigger;
ds@1952
    89
		m_PubTrigger = sm_strdup(value);
ds@1952
    90
		m_PubTriggerSize = strlen(m_PubTrigger);
ds@1952
    91
		return ConfigResult_Accept;
ds@1952
    92
	}
ds@1952
    93
	else if (strcmp(key, "SilentChatTrigger") == 0)
ds@1952
    94
	{
ds@1952
    95
		delete [] m_PrivTrigger;
ds@1952
    96
		m_PrivTrigger = sm_strdup(value);
ds@1952
    97
		m_PrivTriggerSize = strlen(m_PrivTrigger);
ds@1952
    98
		return ConfigResult_Accept;
ds@1952
    99
	}
ds@1952
   100
	else if (strcmp(key, "SilentFailSuppress") == 0)
ds@1952
   101
	{
ds@1952
   102
		g_bSupressSilentFails = strcmp(value, "yes") == 0;
ds@1952
   103
		return ConfigResult_Accept;
ds@1952
   104
	}
ds@1952
   105
ds@1952
   106
	return ConfigResult_Ignore;
ds@1952
   107
}
ds@1952
   108
ds@1952
   109
void ChatTriggers::OnSourceModAllInitialized()
ds@1952
   110
{
ds@1952
   111
	m_pShouldFloodBlock = g_Forwards.CreateForward("OnClientFloodCheck", ET_Event, 1, NULL, Param_Cell);
ds@1952
   112
	m_pDidFloodBlock = g_Forwards.CreateForward("OnClientFloodResult", ET_Event, 2, NULL, Param_Cell, Param_Cell);
ds@1952
   113
}
ds@1952
   114
ds@1952
   115
void ChatTriggers::OnSourceModAllInitialized_Post()
ds@1952
   116
{
ds@1952
   117
	unsigned int file_id;
ds@1952
   118
	
ds@1952
   119
	file_id = g_Translator.FindOrAddPhraseFile("antiflood.phrases.txt");
ds@1952
   120
	g_pFloodPhrases = g_Translator.GetFileByIndex(file_id);
ds@1952
   121
}
ds@1952
   122
ds@1952
   123
void ChatTriggers::OnSourceModGameInitialized()
ds@1952
   124
{
ds@1952
   125
	unsigned int total = 2;
ds@1952
   126
	ConCommandBase *pCmd = icvar->GetCommands();
ds@1952
   127
	const char *name;
ds@1952
   128
	while (pCmd)
ds@1952
   129
	{
ds@1952
   130
		if (pCmd->IsCommand())
ds@1952
   131
		{
ds@1952
   132
			name = pCmd->GetName();
ds@1952
   133
			if (!m_pSayCmd && strcmp(name, "say") == 0)
ds@1952
   134
			{
ds@1952
   135
				m_pSayCmd = (ConCommand *)pCmd;
ds@1952
   136
				if (--total == 0)
ds@1952
   137
				{
ds@1952
   138
					break;
ds@1952
   139
				}
ds@1952
   140
			} else if (!m_pSayTeamCmd && strcmp(name, "say_team") == 0) {
ds@1952
   141
				m_pSayTeamCmd = (ConCommand *)pCmd;
ds@1952
   142
				if (--total == 0)
ds@1952
   143
				{
ds@1952
   144
					break;
ds@1952
   145
				}
ds@1952
   146
			}
ds@1952
   147
		}
ds@1952
   148
		pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
ds@1952
   149
	}
ds@1952
   150
ds@1952
   151
	if (m_pSayCmd)
ds@1952
   152
	{
ds@1952
   153
		SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
ds@1952
   154
		SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
ds@1952
   155
	}
ds@1952
   156
	if (m_pSayTeamCmd)
ds@1952
   157
	{
ds@1952
   158
		SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
ds@1952
   159
		SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
ds@1952
   160
	}
ds@1952
   161
}
ds@1952
   162
ds@1952
   163
void ChatTriggers::OnSourceModShutdown()
ds@1952
   164
{
ds@1952
   165
	if (m_pSayTeamCmd)
ds@1952
   166
	{
ds@1952
   167
		SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
ds@1952
   168
		SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
ds@1952
   169
	}
ds@1952
   170
	if (m_pSayCmd)
ds@1952
   171
	{
ds@1952
   172
		SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
ds@1952
   173
		SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
ds@1952
   174
	}
ds@1952
   175
ds@1952
   176
	g_Forwards.ReleaseForward(m_pShouldFloodBlock);
ds@1952
   177
	g_Forwards.ReleaseForward(m_pDidFloodBlock);
ds@1952
   178
}
ds@1952
   179
ds@1952
   180
#if defined ORANGEBOX_BUILD
ds@1952
   181
void ChatTriggers::OnSayCommand_Pre(const CCommand &command)
ds@1952
   182
{
ds@1952
   183
#else
ds@1952
   184
void ChatTriggers::OnSayCommand_Pre()
ds@1952
   185
{
ds@1952
   186
	CCommand command;
ds@1952
   187
#endif
ds@1952
   188
	int client;
ds@1952
   189
	CPlayer *pPlayer;
ds@1952
   190
	
ds@1952
   191
	client = g_ConCmds.GetCommandClient();
ds@1952
   192
	m_bIsChatTrigger = false;
ds@1952
   193
	m_bWasFloodedMessage = false;
ds@1952
   194
ds@1952
   195
	/* The server console cannot do this */
ds@1952
   196
	if (client == 0 || (pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
ds@1952
   197
	{
ds@1952
   198
		RETURN_META(MRES_IGNORED);
ds@1952
   199
	}
ds@1952
   200
ds@1952
   201
	/* We guarantee the client is connected */
ds@1952
   202
	if (!pPlayer->IsConnected())
ds@1952
   203
	{
ds@1952
   204
		RETURN_META(MRES_IGNORED);
ds@1952
   205
	}
ds@1952
   206
ds@1952
   207
	const char *args = command.ArgS();
ds@1952
   208
	
ds@1952
   209
	if (!args)
ds@1952
   210
	{
ds@1952
   211
		RETURN_META(MRES_IGNORED);
ds@1952
   212
	}
ds@1952
   213
ds@1952
   214
	/* Check if we need to block this message from being sent */
ds@1952
   215
	if (ClientIsFlooding(client))
ds@1952
   216
	{
ds@1952
   217
		char buffer[128];
ds@1952
   218
ds@1952
   219
		/* :TODO: log an error? */
ds@1952
   220
		if (g_Translator.CoreTransEx(g_pFloodPhrases, 
ds@1952
   221
			client, 
ds@1952
   222
			buffer, 
ds@1952
   223
			sizeof(buffer),
ds@1952
   224
			"Flooding the server",
ds@1952
   225
			NULL,
ds@1952
   226
			NULL)
ds@1952
   227
			!= Trans_Okay)
ds@1952
   228
		{
ds@1952
   229
			UTIL_Format(buffer, sizeof(buffer), "You are flooding the server!");
ds@1952
   230
		}
ds@1952
   231
ds@1952
   232
		/* :TODO: we should probably kick people who spam too much. */
ds@1952
   233
ds@1952
   234
		char fullbuffer[192];
ds@1952
   235
		UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s", buffer);
ds@1952
   236
		g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer);
ds@1952
   237
ds@1952
   238
		m_bWasFloodedMessage = true;
ds@1952
   239
ds@1952
   240
		RETURN_META(MRES_SUPERCEDE);
ds@1952
   241
	}
ds@1952
   242
ds@1952
   243
	/* Handle quoted string sets */
ds@1952
   244
	bool is_quoted = false;
ds@1952
   245
	if (args[0] == '"')
ds@1952
   246
	{
ds@1952
   247
		args++;
ds@1952
   248
		is_quoted = true;
ds@1952
   249
	}
ds@1952
   250
ds@1952
   251
	bool is_trigger = false;
ds@1952
   252
	bool is_silent = false;
ds@1952
   253
ds@1952
   254
	/* Check for either trigger */
ds@1952
   255
	if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
ds@1952
   256
	{
ds@1952
   257
		is_trigger = true;
ds@1952
   258
		args = &args[m_PubTriggerSize];
ds@1952
   259
	} 
ds@1952
   260
	else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0) 
ds@1952
   261
	{
ds@1952
   262
		is_trigger = true;
ds@1952
   263
		is_silent = true;
ds@1952
   264
		args = &args[m_PrivTriggerSize];
ds@1952
   265
	}
ds@1952
   266
ds@1952
   267
	if (!is_trigger)
ds@1952
   268
	{
ds@1952
   269
		RETURN_META(MRES_IGNORED);
ds@1952
   270
	}
ds@1952
   271
ds@1952
   272
	/**
ds@1952
   273
	 * Test if this is actually a command!
ds@1952
   274
	 */
ds@1952
   275
	if (!PreProcessTrigger(engine->PEntityOfEntIndex(client), args, is_quoted))
ds@1952
   276
	{
ds@1952
   277
		CPlayer *pPlayer;
ds@1952
   278
		if (is_silent 
ds@1952
   279
			&& g_bSupressSilentFails 
ds@1952
   280
			&& client != 0
ds@1952
   281
			&& (pPlayer = g_Players.GetPlayerByIndex(client)) != NULL
ds@1952
   282
			&& pPlayer->GetAdminId() != INVALID_ADMIN_ID)
ds@1952
   283
		{
ds@1952
   284
			RETURN_META(MRES_SUPERCEDE);
ds@1952
   285
		}
ds@1952
   286
		RETURN_META(MRES_IGNORED);
ds@1952
   287
	}
ds@1952
   288
ds@1952
   289
	m_bIsChatTrigger = true;
ds@1952
   290
ds@1952
   291
	/**
ds@1952
   292
	 * We'll execute it in post.
ds@1952
   293
	 */
ds@1952
   294
	m_bWillProcessInPost = true;
ds@1952
   295
	m_bTriggerWasSilent = is_silent;
ds@1952
   296
ds@1952
   297
	/* If we're silent, block */
ds@1952
   298
	if (is_silent)
ds@1952
   299
	{
ds@1952
   300
		RETURN_META(MRES_SUPERCEDE);
ds@1952
   301
	}
ds@1952
   302
ds@1952
   303
	/* Otherwise, let the command continue */
ds@1952
   304
	RETURN_META(MRES_IGNORED);
ds@1952
   305
}
ds@1952
   306
ds@1952
   307
#if defined ORANGEBOX_BUILD
ds@1952
   308
void ChatTriggers::OnSayCommand_Post(const CCommand &command)
ds@1952
   309
#else
ds@1952
   310
void ChatTriggers::OnSayCommand_Post()
ds@1952
   311
#endif
ds@1952
   312
{
ds@1952
   313
	m_bIsChatTrigger = false;
ds@1952
   314
	m_bWasFloodedMessage = false;
ds@1952
   315
	if (m_bWillProcessInPost)
ds@1952
   316
	{
ds@1952
   317
		/* Reset this for re-entrancy */
ds@1952
   318
		m_bWillProcessInPost = false;
ds@1952
   319
		
ds@1952
   320
		/* Execute the cached command */
ds@1952
   321
		int client = g_ConCmds.GetCommandClient();
ds@1952
   322
		unsigned int old = SetReplyTo(SM_REPLY_CHAT);
ds@1952
   323
		serverpluginhelpers->ClientCommand(engine->PEntityOfEntIndex(client), m_ToExecute);
ds@1952
   324
		SetReplyTo(old);
ds@1952
   325
	}
ds@1952
   326
}
ds@1952
   327
ds@1952
   328
bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
ds@1952
   329
{
ds@1952
   330
	/* Extract a command.  This is kind of sloppy. */
ds@1952
   331
	char cmd_buf[64];
ds@1952
   332
	size_t cmd_len = 0;
ds@1952
   333
	const char *inptr = args;
ds@1952
   334
	while (*inptr != '\0' 
ds@1952
   335
			&& !textparsers->IsWhitespace(inptr) 
ds@1952
   336
			&& *inptr != '"'
ds@1952
   337
			&& cmd_len < sizeof(cmd_buf) - 1)
ds@1952
   338
	{
ds@1952
   339
		cmd_buf[cmd_len++] = *inptr++;
ds@1952
   340
	}
ds@1952
   341
	cmd_buf[cmd_len] = '\0';
ds@1952
   342
ds@1952
   343
	if (cmd_len == 0)
ds@1952
   344
	{
ds@1952
   345
		return false;
ds@1952
   346
	}
ds@1952
   347
ds@1952
   348
	/* See if we have this registered */
ds@1952
   349
	bool prepended = false;
ds@1952
   350
	if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
ds@1952
   351
	{
ds@1952
   352
		/* Check if we had an "sm_" prefix */
ds@1952
   353
		if (strncmp(cmd_buf, "sm_", 3) == 0)
ds@1952
   354
		{
ds@1952
   355
			return false;
ds@1952
   356
		}
ds@1952
   357
ds@1952
   358
		/* Now, prepend.  Don't worry about the buffers.  This will 
ds@1952
   359
		 * work because the sizes are limited from earlier.
ds@1952
   360
		 */
ds@1952
   361
		char new_buf[80];
ds@1952
   362
		strcpy(new_buf, "sm_");
ds@1952
   363
		strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
ds@1952
   364
ds@1952
   365
		/* Recheck */
ds@1952
   366
		if (!g_ConCmds.LookForSourceModCommand(new_buf))
ds@1952
   367
		{
ds@1952
   368
			return false;
ds@1952
   369
		}
ds@1952
   370
		
ds@1952
   371
		prepended = true;
ds@1952
   372
	}
ds@1952
   373
ds@1952
   374
	/* See if we need to do extra string manipulation */
ds@1952
   375
	if (is_quoted || prepended)
ds@1952
   376
	{
ds@1952
   377
		size_t len;
ds@1952
   378
ds@1952
   379
		/* Check if we need to prepend sm_ */
ds@1952
   380
		if (prepended)
ds@1952
   381
		{
ds@1952
   382
			len = UTIL_Format(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args);
ds@1952
   383
		} else {
ds@1952
   384
			len = strncopy(m_ToExecute, args, sizeof(m_ToExecute));
ds@1952
   385
		}
ds@1952
   386
		
ds@1952
   387
		/* Check if we need to strip a quote */
ds@1952
   388
		if (is_quoted)
ds@1952
   389
		{
ds@1952
   390
			if (m_ToExecute[len-1] == '"')
ds@1952
   391
			{
ds@1952
   392
				m_ToExecute[--len] = '\0';
ds@1952
   393
			}
ds@1952
   394
		}
ds@1952
   395
	} else {
ds@1952
   396
		strncopy(m_ToExecute, args, sizeof(m_ToExecute));
ds@1952
   397
	}
ds@1952
   398
ds@1952
   399
	return true;
ds@1952
   400
}
ds@1952
   401
ds@1952
   402
unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
ds@1952
   403
{
ds@1952
   404
	unsigned int old = m_ReplyTo;
ds@1952
   405
ds@1952
   406
	m_ReplyTo = reply;
ds@1952
   407
ds@1952
   408
	return old;
ds@1952
   409
}
ds@1952
   410
ds@1952
   411
unsigned int ChatTriggers::GetReplyTo()
ds@1952
   412
{
ds@1952
   413
	return m_ReplyTo;
ds@1952
   414
}
ds@1952
   415
ds@1952
   416
bool ChatTriggers::IsChatTrigger()
ds@1952
   417
{
ds@1952
   418
	return m_bIsChatTrigger;
ds@1952
   419
}
ds@1952
   420
ds@1952
   421
bool ChatTriggers::ClientIsFlooding(int client)
ds@1952
   422
{
ds@1952
   423
	bool is_flooding = false;
ds@1952
   424
ds@1952
   425
	if (m_pShouldFloodBlock->GetFunctionCount() != 0)
ds@1952
   426
	{
ds@1952
   427
		cell_t res = 0;
ds@1952
   428
ds@1952
   429
		m_pShouldFloodBlock->PushCell(client);
ds@1952
   430
		m_pShouldFloodBlock->Execute(&res);
ds@1952
   431
ds@1952
   432
		if (res != 0)
ds@1952
   433
		{
ds@1952
   434
			is_flooding = true;
ds@1952
   435
		}
ds@1952
   436
	}
ds@1952
   437
ds@1952
   438
	if (m_pDidFloodBlock->GetFunctionCount() != 0)
ds@1952
   439
	{
ds@1952
   440
		m_pDidFloodBlock->PushCell(client);
ds@1952
   441
		m_pDidFloodBlock->PushCell(is_flooding ? 1 : 0);
ds@1952
   442
		m_pDidFloodBlock->Execute(NULL);
ds@1952
   443
	}
ds@1952
   444
ds@1952
   445
	return is_flooding;
ds@1952
   446
}
ds@1952
   447
ds@1952
   448
bool ChatTriggers::WasFloodedMessage()
ds@1952
   449
{
ds@1952
   450
	return m_bWasFloodedMessage;
ds@1952
   451
}