[ANY] Chat Responder
ABOUT:
This plugin takes triggers and responces from a config file and when a player types a phrase, it'll check to see if there are any triggers in it. If so, it'll print back. It can be configured to detect "exact" or "contains" phrases.
If I type "Hey whats up!" and "Hey whats up!" is registered as an exact trigger, it'll cause a response.
If I type "Banana Man yo" and "yo" is registered as a "contains" trigger, then it'll cause a responce.
The config file looks like this
Where "Hi" will trigger the response and "type" will determine if it's supposed to be an exact or a contain trigger.
CREDITS:
Thanks to ThatOneGuy for introducing me to ADT Arrays when I was learning and also shout out to this thread for giving me the idea [link]
BUGS:
If there are any bugs please comment below, don't forget to check your error logs!
SOURCE:
PHP Code:
/****************************************************************************
[ANY] Chat Responder - A friend inside the code *
Copyright (C) 2015 Michael Flaherty *
This program is free software: you can redistribute it and/or modify *
it under the terms of the GNU General Public License as published by *
the Free Software Foundation, either version 3 of the License, or *
(at your option) any later version. *
This program is distributed in the hope that it will be useful, *
but WITHOUT ANY WARRANTY; without even the implied warranty of *
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
GNU General Public License for more details. *
You should have received a copy of the GNU General Public License *
along with this program. If not, see <http://www.gnu.org/licenses/> *
*****************************************************************************/
#include <sourcemod>
#include <autoexecconfig>
#include <colorvariables>
#pragma semicolon 1
#pragma newdecls required
#pragma dynamic 131072 //increase stack space to from 4 kB to 131072 cells (or 512KB, a cell is 4 bytes).
#define TAG " \x04"
#define PLUGIN_VERSION "1.3.4"
#define MAX_TEXT_LENGTH 512
/* ADT Arrays */
Handle gh_adtExactTextsResponse;
Handle gh_adtExactTextsTrigger;
Handle gh_adtContainTextTrigger;
Handle gh_adtContainTextResponse;
/* Bools */
bool g_bLateLoad;
bool ga_bCooldown[MAXPLAYERS + 1];
/* Strings */
char g_sPath[PLATFORM_MAX_PATH];
/* ConVars */
ConVar gcv_bPluginEnabled;
ConVar gcv_sFilePath;
ConVar gcv_bGlobalReply;
ConVar gcv_fCoolDownTime;
public Plugin myinfo =
{
name = "[ANY] Chat Responder",
author = "Headline",
description = "A simple chat bot",
version = PLUGIN_VERSION,
url = "http://www.michaelwflaherty.com"
};
public APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int err_max)
{
g_bLateLoad = bLate;
return APLRes_Success;
}
public void OnPluginStart()
{
AutoExecConfig_SetFile("hl_chatresponder");
AutoExecConfig_CreateConVar("hl_chatresponder_version", PLUGIN_VERSION, "Headline's Chat Responder : Version", FCVAR_NOTIFY|FCVAR_DONTRECORD);
gcv_bPluginEnabled = AutoExecConfig_CreateConVar("hl_chatresponder_enable", "1", "Enable the plugin?\n (1 = Yes, 0 = No)", FCVAR_NONE, true, 0.0, true, 1.0);
gcv_sFilePath = AutoExecConfig_CreateConVar("hl_chatresponder_filepath", "configs/hl_chatresponder.txt", "File path to parse", FCVAR_NONE);
gcv_bGlobalReply = AutoExecConfig_CreateConVar("hl_chatresponder_global_reply", "1", "Make it so the Bot's responses are visible to everyone?\n (1 = Yes, 0 = No)", FCVAR_NONE, true, 0.0, true, 1.0);
gcv_fCoolDownTime = AutoExecConfig_CreateConVar("hl_chatresponder_cooldown_time", "10.0", "Cooldown time for chat responses. (to prevent spam)", FCVAR_NONE, true, 0.0, true, 1.0);
AutoExecConfig_ExecuteFile();
AutoExecConfig_CleanFile();
if(g_bLateLoad)
{
OnConfigsExecuted();
}
AddCommandListener(OnSay, "say");
AddCommandListener(OnSay, "say_team");
}
public void OnConfigsExecuted()
{
if (gcv_bPluginEnabled.BoolValue)
{
InitializeADTArrays();
}
}
public void OnClientConnected(int client)
{
ga_bCooldown[client] = false;
}
public void OnClientDisconnect(int client)
{
ga_bCooldown[client] = false;
}
public Action OnSay(int client, const char[] command, int args)
{
if (!gcv_bPluginEnabled.BoolValue)
{
return Plugin_Continue;
}
if (!IsValidClient(client))
{
return Plugin_Continue;
}
else
{
char sText[4096];
GetCmdArgString(sText, sizeof(sText));
StripQuotes(sText);
Handle hData = CreateDataPack();
WritePackCell(hData, client);
WritePackString(hData, sText);
ResetPack(hData);
CreateTimer(0.3, Timer_Reply, hData, TIMER_FLAG_NO_MAPCHANGE);
}
return Plugin_Continue;
}
public Action Timer_Reply(Handle hTimer, Handle hData)
{
char sText[4096];
int client = ReadPackCell(hData);
ReadPackString(hData, sText, sizeof(sText));
CloseHandle(hData);
if (IsValidClient(client))
{
char sTrigger[128];
char sResponse[MAX_TEXT_LENGTH];
bool bFoundResponse = false;
int iArrayIndex = FindStringInArray(gh_adtExactTextsTrigger, sText);
if (iArrayIndex != -1)
{
GetArrayString(gh_adtExactTextsResponse, iArrayIndex, sResponse, sizeof(sResponse));
if (StrContains(sResponse, "{NAME}"))
{
char sName[MAX_NAME_LENGTH];
GetClientName(client, sName, sizeof(sName));
ReplaceString(sResponse, sizeof(sResponse), "{NAME}", sName);
}
int count = 0;
for (int j = 0; sResponse[j]; j++) {
if (sResponse[j] == '\n')
count++;
}
char[][] lines = new char[count+1][MAX_TEXT_LENGTH];
ExplodeString(sResponse, "\n", lines, count+1, MAX_TEXT_LENGTH);
if (!ga_bCooldown[client])
{
for (int j = 0; j < count+1; j++)
{
if (gcv_bGlobalReply.BoolValue)
{
CPrintToChatAll("%s%s", TAG, lines[j]);
}
else
{
CPrintToChat(client, "%s%s", TAG, lines[j]);
}
}
}
else
{
PrintToChat(client, "%sDo not spam the chat responder!", TAG);
}
bFoundResponse = true;
}
else
{
bFoundResponse = false;
}
if (!bFoundResponse)
{
for (int i = 0; i < GetArraySize(gh_adtContainTextTrigger); i++)
{
GetArrayString(gh_adtContainTextTrigger, i, sTrigger, sizeof(sTrigger));
if (StrContains(sText, sTrigger, false) != -1)
{
GetArrayString(gh_adtContainTextResponse, i, sResponse, sizeof(sResponse));
int count = 0;
for (int j = 0; sResponse[j]; j++) {
if (sResponse[j] == '\n')
count++;
}
char[][] lines = new char[count+1][MAX_TEXT_LENGTH];
ExplodeString(sResponse, "\n", lines, count+1, MAX_TEXT_LENGTH);
if (!ga_bCooldown[client])
{
for (int j = 0; j < count+1; j++)
{
if (gcv_bGlobalReply.BoolValue)
{
CPrintToChatAll("%s%s", TAG, lines[j]);
}
else
{
CPrintToChat(client, "%s%s", TAG, lines[j]);
}
}
}
else
{
PrintToChat(client, "%sDo not spam the chat responder!", TAG);
}
bFoundResponse = true;
break;
}
}
}
if (bFoundResponse && gcv_fCoolDownTime.FloatValue > 0.1)
{
CreateTimer(gcv_fCoolDownTime.FloatValue, Timer_Cooldown, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
ga_bCooldown[client] = true;
}
}
}
public Action Timer_Cooldown(Handle hTimer, int iUserID)
{
int client = GetClientOfUserId(iUserID);
if (IsValidClient(client))
{
if (ga_bCooldown[client])
{
ga_bCooldown[client] = false;
}
}
}
public void ParseKvFile()
{
char sPath[PLATFORM_MAX_PATH];
gcv_sFilePath.GetString(sPath, sizeof(sPath));
BuildPath(Path_SM, g_sPath, sizeof(g_sPath), sPath);
Handle hKeyValues = CreateKeyValues("Chat Responder");
if(!FileExists(g_sPath))
{
SetFailState("Configuration file %s not found!", g_sPath);
return;
}
if(!FileToKeyValues(hKeyValues, g_sPath))
{
SetFailState("Improper structure for configuration file %s!", g_sPath);
return;
}
if(KvGotoFirstSubKey(hKeyValues))
{
do
{
char sSectionName[128];
char sType[128];
char sText[MAX_TEXT_LENGTH];
KvGetSectionName(hKeyValues, sSectionName, sizeof(sSectionName)); //get name of section
KvGetString(hKeyValues, "response", sText, sizeof(sText));
KvGetString(hKeyValues, "type", sType, sizeof(sType)); //call key-value by name of key and store value in variable, with default as "blah" if it doesnt exist
ReplaceString(sText, sizeof(sText), "{new_line}", "\n");
if (StrEqual(sType, "e", false))
{
PushArrayString(gh_adtExactTextsTrigger, sSectionName);
PushArrayString(gh_adtExactTextsResponse, sText);
}
else if (StrEqual(sType, "c", false))
{
PushArrayString(gh_adtContainTextTrigger, sSectionName);
PushArrayString(gh_adtContainTextResponse, sText);
}
}
while(KvGotoNextKey(hKeyValues, false));
KvGoBack(hKeyValues);
}
else
{
SetFailState("Can't find first subkey in configuration file %s!", g_sPath);
CloseHandle(hKeyValues);
return;
}
CloseHandle(hKeyValues);
}
stock void InitializeADTArrays()
{
gh_adtExactTextsTrigger = CreateArray(64);
gh_adtExactTextsResponse = CreateArray(MAX_TEXT_LENGTH);
ClearArray(gh_adtExactTextsTrigger);
ClearArray(gh_adtExactTextsResponse);
gh_adtContainTextTrigger = CreateArray(64);
gh_adtContainTextResponse = CreateArray(MAX_TEXT_LENGTH);
ClearArray(gh_adtContainTextTrigger);
ClearArray(gh_adtContainTextResponse);
ParseKvFile();
}
stock bool IsValidClient(int client, bool bAllowBots = false, bool bAllowDead = true)
{
if(!(1 <= client <= MaxClients) || !IsClientInGame(client) || (IsFakeClient(client) && !bAllowBots) || IsClientSourceTV(client) || IsClientReplay(client) || (!bAllowDead && !IsPlayerAlive(client)))
{
return false;
}
return true;
}
/*
Change log
1.0
* Initial release
1.1
* Fixed some dumb mistakes
* Added a cooldown timer to prevent spam
* Added a few IsValidClient checks to make sure we don't get errors in error logs
* Implemented some of ThatOneGuy's suggestions
1.3.3
* Fixed {new_line}
1.3.4
* Fixed issue where client's cooldowns would not be reset
*/
Last edited by headline; 03-03-2020 at 23:11.
|