From 877989932d8f94fca845f1e43ef974ed1f4804eb Mon Sep 17 00:00:00 2001 From: neon Date: Mon, 23 Jul 2018 11:02:35 +0200 Subject: [PATCH] initial commit --- Discord_Unloze/scripting/Discord_Unloze.sp | 862 +++++++++++++++++++++ includes/AntiBhopCheat.inc | 6 + includes/calladmin.inc | 289 +++++++ 3 files changed, 1157 insertions(+) create mode 100644 Discord_Unloze/scripting/Discord_Unloze.sp create mode 100644 includes/AntiBhopCheat.inc create mode 100644 includes/calladmin.inc diff --git a/Discord_Unloze/scripting/Discord_Unloze.sp b/Discord_Unloze/scripting/Discord_Unloze.sp new file mode 100644 index 00000000..b269b340 --- /dev/null +++ b/Discord_Unloze/scripting/Discord_Unloze.sp @@ -0,0 +1,862 @@ +#pragma semicolon 1 + +#include +#include +#include +#include +#include +#include +#include "sdktools_functions.inc" + + +#include +#include +#include + +#pragma newdecls required + +#define STEAM_API_KEY "7FF6DCA2152A102DFF8CEC89D917B2B2" +#define DISCORD_LIVEWEBHOOK_URL "https://discordapp.com/api/webhooks/420234463335677961/HC5Sd_WJu4fGq1SlwOfvHLPcpCMoNAniITYAbxz2gAT80VAFin8TWK8P9x0xQSJgLXRj" +#define DISCORD_ADMINLOGS_WEBHOOKURL "https://discordapp.com/api/webhooks/420234772254687233/pgqnmLXwR8bTe5SI_EHU5ZbbyITQxBB5FsiyyDzvhQS5LZLsyljzfFGDPpY55aH5H-Lp" +#define DISCORD_ADMINCHAT_WEBHOOKURL "https://discordapp.com/api/webhooks/420234615173939220/4k2ejMnksUxS_Fv0UtTazdoWfOVizxli4Bz97PHhw4vgPkkOuG_nNuFNpmsfxCs6XFbm" +#define DISCORD_RCON_WEBHOOKURL "https://discordapp.com/api/webhooks/421078039699521536/WHHvLf4DkY8UUR_C0BjdCk1REbu5jugAzOrr0MU2nvrhR0hxn6OtlHhHgi-dR7VmfEaT" +#define DISCORD_CALLADMIN_WEBHOOKURL "https://discordapp.com/api/webhooks/423998266544357376/1LeQ5yxlsrAvi8MfgmE1HaI_IHDn419lXefwIS6WveyTa3dbZbPhNOs5cDnc8dLATeNv" +#define DISCORD_ANTIBHOPCHEAT_WEBHOOKURL "https://discordapp.com/api/webhooks/424725421632782353/76taTm6PgfeT02oX_yTgM-WEESAFp5uWxwZCoFnrW23cX8hxg9l-9mZTbCz-uI579n_9" +#define DISCORD_ENTWATCH_WEBHOOKURL "https://discordapp.com/api/webhooks/428546704195715072/s18ixMOKzadul8OwuQ3wONb3bcsjh-CIPq3jg-g2Ljhc50JpWiepwUWewJQybavpuaOv" + + +Regex g_Regex_Clyde = null; + +ArrayList g_arrQueuedMessages = null; + +Handle g_hDataTimer = null; +Handle g_hReplaceConfigFile = null; + +UserMsg g_umSayText2 = INVALID_MESSAGE_ID; + +bool g_bLoadedLate; +bool g_bProcessingData; +bool g_bGotReplaceFile; +bool g_bTeamChat; + +char g_sReplacePath[PLATFORM_MAX_PATH]; +char g_sAvatarURL[MAXPLAYERS + 1][128]; + +int g_iRatelimitRemaining = 5; +int g_iRatelimitReset; + +int g_iLastReportID; + +public Plugin myinfo = +{ + name = "Discord core", + author = "Obus and Neon", + description = "Chat- & Rcon-Support", + version = "1.2.0", + url = "" +} + +public APLRes AskPluginLoad2(Handle hThis, bool bLate, char[] sError, int err_max) +{ + g_bLoadedLate = bLate; + + return APLRes_Success; +} + +public void OnPluginStart() +{ + char sRegexErr[32]; + RegexError RegexErr; + + g_Regex_Clyde = CompileRegex(".*(clyde).*", PCRE_CASELESS, sRegexErr, sizeof(sRegexErr), RegexErr); + + if (RegexErr != REGEX_ERROR_NONE) + LogError("Could not compile \"Clyde\" regex (err: %s)", sRegexErr); + + g_hReplaceConfigFile = CreateKeyValues("AutoReplace"); + BuildPath(Path_SM, g_sReplacePath, sizeof(g_sReplacePath), "configs/custom-chatcolorsreplace.cfg"); + + if (FileToKeyValues(g_hReplaceConfigFile, g_sReplacePath)) + g_bGotReplaceFile = true; + + g_arrQueuedMessages = CreateArray(ByteCountToCells(1024)); + + g_hDataTimer = CreateTimer(0.333, Timer_DataProcessor, INVALID_HANDLE, TIMER_REPEAT); + + g_umSayText2 = GetUserMessageId("SayText2"); + + if (g_umSayText2 == INVALID_MESSAGE_ID) + SetFailState("This game doesn't support SayText2 user messages."); + + HookUserMessage(g_umSayText2, Hook_UserMessage, false); + + HookEvent("player_say", EventHook_PlayerSay, EventHookMode_Post); + + if (g_bLoadedLate) + { + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientAuthorized(i)) + continue; + + static char sAuthID32[32]; + + GetClientAuthId(i, AuthId_Steam2, sAuthID32, sizeof(sAuthID32)); + OnClientAuthorized(i, sAuthID32); + } + } + + AddCommandListener(CommandListener_SmChat, "sm_chat"); + + RegServerCmd("sm_printtoadminchat", Command_PrintToAdminChat, "Discord Integration"); + RegServerCmd("sm_printtoallchat", Command_PrintToAllChat, "Discord Integration"); +} + +public void OnPluginEnd() +{ + delete g_arrQueuedMessages; + delete g_hDataTimer; + + UnhookUserMessage(g_umSayText2, Hook_UserMessage, false); + UnhookEvent("player_say", EventHook_PlayerSay, EventHookMode_Post); +} + +public void OnClientAuthorized(int client, const char[] sAuthID32) +{ + if (IsFakeClient(client)) + return; + + char sAuthID64[32]; + + if (!Steam32IDtoSteam64ID(sAuthID32, sAuthID64, sizeof(sAuthID64))) + return; + + static char sRequest[256]; + + FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=%s&steamids=%s&format=vdf", STEAM_API_KEY, sAuthID64); + + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); + + if (!hRequest || + !SteamWorks_SetHTTPRequestContextValue(hRequest, client) || + !SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + delete hRequest; + } +} + +public Action Command_PrintToAdminChat(int args) +{ + char sArgs[1024]; + char sArgs2[1024]; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + GetCmdArg(2, sArgs2, sizeof(sArgs2)); + + for(int i = 0; i < MAXPLAYERS; i++) + { + if (IsValidClient(i)) + { + bool bAdmin = CheckCommandAccess(i, "", ADMFLAG_GENERIC, true); + if (bAdmin) + CPrintToChat(i, "{azure}[DISCORD](ADMINS) %s: {white}%s", sArgs, sArgs2); + } + } + return Plugin_Handled; +} + +public Action Command_PrintToAllChat(int args) +{ + char sArgs[1024]; + char sArgs2[1024]; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + GetCmdArg(2, sArgs2, sizeof(sArgs2)); + + CPrintToChatAll("{azure}[DISCORD](ALL) %s: {white}%s", sArgs, sArgs2); + + return Plugin_Handled; +} + +public Action Timer_DataProcessor(Handle hThis) +{ + if (!g_bProcessingData) + return; + + if (g_iRatelimitRemaining == 0 && GetTime() < g_iRatelimitReset) + return; + + //PrintToServer("[Timer_DataProcessor] Array Length #1: %d", g_arrQueuedMessages.Length); + + char sContent[1024]; + g_arrQueuedMessages.GetString(0, sContent, sizeof(sContent)); + g_arrQueuedMessages.Erase(0); + + char sURL[128]; + g_arrQueuedMessages.GetString(0, sURL, sizeof(sURL)); + g_arrQueuedMessages.Erase(0); + + if (g_arrQueuedMessages.Length == 0) + g_bProcessingData = false; + + //PrintToServer("[Timer_DataProcessor] Array Length #2: %d", g_arrQueuedMessages.Length); + + //PrintToServer("%s | %s", sURL, sContent); + + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, sURL); + + JSONObject RequestJSON = view_as(json_load(sContent)); + + if (!hRequest || + !SteamWorks_SetHTTPRequestContextValue(hRequest, RequestJSON) || + !SteamWorks_SetHTTPCallbacks(hRequest, OnHTTPRequestCompleted) || + !SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/json", sContent, strlen(sContent)) || + !SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 10) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + LogError("Discord SteamWorks_CreateHTTPRequest failed."); + + delete RequestJSON; + delete hRequest; + + return; + } +} +public Action Hook_UserMessage(UserMsg msg_id, Handle bf, const players[], int playersNum, bool reliable, bool init) +{ + char sMessageName[32]; + char sMessageSender[64]; + int iAuthor = BfReadByte(bf); + bool bIsChat = view_as(BfReadByte(bf)); if (bIsChat) bIsChat=false; //fucking compiler shut the fuck up REEEEEE + BfReadString(bf, sMessageName, sizeof(sMessageName), false); + BfReadString(bf, sMessageSender, sizeof(sMessageSender), false); + + if (iAuthor <= 0 || iAuthor > MaxClients) + return; + + if (strlen(sMessageName) == 0 || strlen(sMessageSender) == 0) + return; + + if (strcmp(sMessageName, "#Cstrike_Name_Change") == 0) + return; + + if (sMessageName[13] == 'C' || sMessageName[13] == 'T' || sMessageName[13] == 'S') + g_bTeamChat = true; + else + g_bTeamChat = false; +} + +public void EventHook_PlayerSay(Event hThis, const char[] sName, bool bDontBroadcast) +{ + int iUserID = GetEventInt(hThis, "userid"); + int iClient = GetClientOfUserId(iUserID); + char sMessageText[192]; + + GetEventString(hThis, "text", sMessageText, sizeof(sMessageText)); + + //PrintToServer("[EventHook_PlayerSay] Fired for %N: %s", iClient, sMessageText); + + TrimString(sMessageText); + + if (strlen(sMessageText) == 0) + return; + + char sClientName[64]; + + GetClientName(iClient, sClientName, sizeof(sClientName)); + + + + if (g_bGotReplaceFile) + { + char sPart[192]; + char sBuff[192]; + int CurrentIndex = 0; + int NextIndex = 0; + + while(NextIndex != -1 && CurrentIndex < sizeof(sMessageText)) + { + NextIndex = BreakString(sMessageText[CurrentIndex], sPart, sizeof(sPart)); + + KvGetString(g_hReplaceConfigFile, sPart, sBuff, sizeof(sBuff), NULL_STRING); + + if(sBuff[0]) + { + ReplaceString(sMessageText[CurrentIndex], sizeof(sMessageText) - CurrentIndex, sPart, sBuff); + CurrentIndex += strlen(sBuff); + } + else + CurrentIndex += NextIndex; + } + } + + if (g_bTeamChat) + { + if (sMessageText[0] == '@') + return; + + char sMessageFinal[256]; + char sTeamName[32]; + + GetTeamName(GetClientTeam(iClient), sTeamName, sizeof(sTeamName)); + + if (sTeamName[0] == 'C') + Format(sMessageFinal, sizeof(sMessageFinal), "(Counter-Terrorist) %s", sMessageText); + else if (sTeamName[0] == 'T') + Format(sMessageFinal, sizeof(sMessageFinal), "(Terrorist) %s", sMessageText); + else + Format(sMessageFinal, sizeof(sMessageFinal), "(Spectator) %s", sMessageText); + + if (g_sAvatarURL[iClient][0] != '\0') + Discord_POST(DISCORD_LIVEWEBHOOK_URL, sMessageFinal, true, sClientName, true, g_sAvatarURL[iClient]); + else + Discord_POST(DISCORD_LIVEWEBHOOK_URL, sMessageFinal, true, sClientName); + + return; + } + + if (g_sAvatarURL[iClient][0] != '\0') + Discord_POST(DISCORD_LIVEWEBHOOK_URL, sMessageText, true, sClientName, true, g_sAvatarURL[iClient]); + else + Discord_POST(DISCORD_LIVEWEBHOOK_URL, sMessageText, true, sClientName); +} + +stock bool Steam32IDtoSteam64ID(const char[] sSteam32ID, char[] sSteam64ID, int Size) +{ + if (strlen(sSteam32ID) < 11 || strncmp(sSteam32ID[0], "STEAM_0:", 8) || strcmp(sSteam32ID, "STEAM_ID_PENDING") == 0) + { + sSteam64ID[0] = 0; + return false; + } + + int iUpper = 765611979; + int isSteam64ID = StringToInt(sSteam32ID[10]) * 2 + 60265728 + sSteam32ID[8] - 48; + + int iDiv = isSteam64ID / 100000000; + int iIdx = 9 - (iDiv ? (iDiv / 10 + 1) : 0); + iUpper += iDiv; + + IntToString(isSteam64ID, sSteam64ID[iIdx], Size - iIdx); + iIdx = sSteam64ID[9]; + IntToString(iUpper, sSteam64ID, Size); + sSteam64ID[9] = iIdx; + + return true; +} + +stock void Discord_MakeStringSafe(const char[] sOrigin, char[] sOut, int iOutSize) +{ + int iDataLen = strlen(sOrigin); + int iCurIndex; + + for (int i = 0; i < iDataLen && iCurIndex < iOutSize; i++) + { + if (sOrigin[i] < 0x20 && sOrigin[i] != 0x0) + { + //sOut[iCurIndex] = 0x20; + //iCurIndex++; + continue; + } + + switch (sOrigin[i]) + { + // case '"': + // { + // strcopy(sOut[iCurIndex], iOutSize, "\\u0022"); + // iCurIndex += 6; + + // continue; + // } + // case '\\': + // { + // strcopy(sOut[iCurIndex], iOutSize, "\\u005C"); + // iCurIndex += 6; + + // continue; + // } + case '@': + { + strcopy(sOut[iCurIndex], iOutSize, "@​"); //@ + zero-width space + iCurIndex += 4; + + continue; + } + case '`': + { + strcopy(sOut[iCurIndex], iOutSize, "\\`"); + iCurIndex += 2; + + continue; + } + case '_': + { + strcopy(sOut[iCurIndex], iOutSize, "\\_"); + iCurIndex += 2; + + continue; + } + case '~': + { + strcopy(sOut[iCurIndex], iOutSize, "\\~"); + iCurIndex += 2; + + continue; + } + default: + { + sOut[iCurIndex] = sOrigin[i]; + iCurIndex++; + } + } + } +} + +stock int OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, int client) +{ + if (bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) + { + if (eStatusCode != k_EHTTPStatusCode429TooManyRequests) + LogError("SteamAPI HTTP Response failed: %d", eStatusCode); + + delete hRequest; + return; + } + + int iBodyLength; + SteamWorks_GetHTTPResponseBodySize(hRequest, iBodyLength); + + char[] sData = new char[iBodyLength]; + SteamWorks_GetHTTPResponseBodyData(hRequest, sData, iBodyLength); + + delete hRequest; + + APIWebResponse(sData, client); +} + +stock void APIWebResponse(const char[] sData, int client) +{ + KeyValues kvResponse = new KeyValues("SteamAPIResponse"); + + if (!kvResponse.ImportFromString(sData, "SteamAPIResponse")) + { + //LogError("kvResponse.ImportFromString(\"SteamAPIResponse\") in APIWebResponse failed."); + + delete kvResponse; + return; + } + + if (!kvResponse.JumpToKey("players")) + { + //LogError("kvResponse.JumpToKey(\"players\") in APIWebResponse failed."); + + delete kvResponse; + return; + } + + if (!kvResponse.GotoFirstSubKey()) + { + //LogError("kvResponse.GotoFirstSubKey() in APIWebResponse failed."); + + delete kvResponse; + return; + } + + kvResponse.GetString("avatarfull", g_sAvatarURL[client], sizeof(g_sAvatarURL[])); + + delete kvResponse; +} + +stock void HTTPPostJSON(const char[] sURL, const char[] sText) +{ + // if (g_iRatelimitRemaining > 0 && !g_bProcessingData && GetTime() < g_iRatelimitReset) + // { + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, sURL); + + JSONObject RequestJSON = view_as(json_load(sText)); + + if (!hRequest || + !SteamWorks_SetHTTPRequestContextValue(hRequest, RequestJSON) || + !SteamWorks_SetHTTPCallbacks(hRequest, OnHTTPRequestCompleted) || + !SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/json", sText, strlen(sText)) || + !SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 15) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + LogError("Discord SteamWorks_CreateHTTPRequest failed."); + + delete RequestJSON; + delete hRequest; + + return; + } + // } + // else + // { + // g_arrQueuedMessages.PushString(sText); + // g_arrQueuedMessages.PushString(sURL); + // g_bProcessingData = true; + // } + + //delete hRequest; +} + +stock void Discord_POST(const char[] sURL, char[] sText, bool bUsingUsername=false, char[] sUsername=NULL_STRING, bool bUsingAvatar=false, char[] sAvatarURL=NULL_STRING, bool bSafe=true, bool bTimestamp=true) +{ + //PrintToServer("[Discord_POST] Called with text: %s", sText); + + JSONRootNode hJSONRoot = new JSONObject(); + + char sSafeText[4096]; + char sFinal[4096]; + + if (bUsingUsername) + { + TrimString(sUsername); + + if (g_Regex_Clyde.Match(sUsername) > 0 || strlen(sUsername) < 2) + (view_as(hJSONRoot)).SetString("username", "Invalid Name"); + else + (view_as(hJSONRoot)).SetString("username", sUsername); + } + + if (bUsingAvatar) + (view_as(hJSONRoot)).SetString("avatar_url", sAvatarURL); + + if (bSafe) + { + Discord_MakeStringSafe(sText, sSafeText, sizeof(sSafeText)); + } + else + { + Format(sSafeText, sizeof(sSafeText), "%s", sText); + } + + if (bTimestamp) + { + int iTime = GetTime(); + char sTime[32]; + FormatTime(sTime, sizeof(sTime), "%r", iTime); + Format(sSafeText, sizeof(sSafeText), "[ *%s* ] %s", sTime, sText); + } + + (view_as(hJSONRoot)).SetString("content", sSafeText); + (view_as(hJSONRoot)).ToString(sFinal, sizeof(sFinal), 0); + + //hJSONRoot.DumpToServer(); + + delete hJSONRoot; + + if ((g_iRatelimitRemaining > 0 || GetTime() >= g_iRatelimitReset) && !g_bProcessingData) + { + //PrintToServer("[Discord_POST] Have allowances and not processing data"); + + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, sURL); + + JSONObject RequestJSON = view_as(json_load(sFinal)); + + if (!hRequest || + !SteamWorks_SetHTTPRequestContextValue(hRequest, RequestJSON) || + !SteamWorks_SetHTTPCallbacks(hRequest, OnHTTPRequestCompleted) || + !SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/json", sFinal, strlen(sFinal)) || + !SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 10) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + LogError("Discord SteamWorks_CreateHTTPRequest failed."); + + delete RequestJSON; + delete hRequest; + + return; + } + } + else + { + //PrintToServer("[Discord_POST] Have allowances? [%s] | Is processing data? [%s]", g_iRatelimitRemaining > 0 ? "YES":"NO", g_bProcessingData?"YES":"NO"); + g_arrQueuedMessages.PushString(sFinal); + g_arrQueuedMessages.PushString(sURL); + g_bProcessingData = true; + } + + //delete hRequest; //nonono +} + +public int OnHTTPRequestCompleted(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, JSONObject RequestJSON) +{ + if (bFailure || !bRequestSuccessful || (eStatusCode != k_EHTTPStatusCode200OK && eStatusCode != k_EHTTPStatusCode204NoContent)) + { + if (eStatusCode != k_EHTTPStatusCode429TooManyRequests) + LogError("Discord HTTP request failed: %d", eStatusCode); + + if (eStatusCode == k_EHTTPStatusCode400BadRequest) + { + char sData[2048]; + + (view_as(RequestJSON)).ToString(sData, sizeof(sData), 0); + + LogError("Malformed request? Dumping request data:\n%s", sData); + } + else if (eStatusCode == k_EHTTPStatusCode429TooManyRequests) + { + g_iRatelimitRemaining = 0; + g_iRatelimitReset = GetTime() + 5; + } + + delete RequestJSON; + delete hRequest; + + return; + } + + static int iLastRatelimitRemaining = 0; + static int iLastRatelimitReset = 0; + char sTmp[32]; + bool bHeaderExists = SteamWorks_GetHTTPResponseHeaderValue(hRequest, "x-ratelimit-remaining", sTmp, sizeof(sTmp)); + + if (!bHeaderExists) + LogError("x-ratelimit-remaining header value could not be retrieved"); + + int iRatelimitRemaining = StringToInt(sTmp); + + bHeaderExists = SteamWorks_GetHTTPResponseHeaderValue(hRequest, "x-ratelimit-reset", sTmp, sizeof(sTmp)); + + if (!bHeaderExists) + LogError("x-ratelimit-reset header value could not be retrieved"); + + int iRatelimitReset = StringToInt(sTmp); + + if (iRatelimitRemaining < iLastRatelimitRemaining || iRatelimitReset >= iLastRatelimitReset) //don't be fooled by different completion times + { + g_iRatelimitRemaining = iRatelimitRemaining; + g_iRatelimitReset = iRatelimitReset; + } + + //PrintToServer("limit: %d | remaining: %d || reset %d - now %d", g_iRatelimitLimit, g_iRatelimitRemaining, g_iRatelimitReset, GetTime()); + + delete RequestJSON; + delete hRequest; +} + +stock bool IsValidClient(int client) +{ + return (client > 0 && client <= MaxClients && IsClientInGame(client)); +} + +public Action OnLogAction(Handle hSource, Identity ident, int client, int target, const char[] sMsg) +{ + if (client <= 0) + return; + + if ((StrContains(sMsg, "sm_psay", false)!= -1) || (StrContains(sMsg, "sm_chat", false)!= -1)) + return;// dont log sm_psay and sm_chat + + char sFinal[256]; + char sCurrentMap[32]; + char sClientName[64]; + + GetCurrentMap(sCurrentMap, sizeof(sCurrentMap)); + Format(sFinal, sizeof(sFinal), "[ %s ]```%s```", sCurrentMap, sMsg); + + GetClientName(client, sClientName, sizeof(sClientName)); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_ADMINLOGS_WEBHOOKURL, sFinal, true, sClientName, true, g_sAvatarURL[client], false); + else + Discord_POST(DISCORD_ADMINLOGS_WEBHOOKURL, sFinal, true, sClientName, false, "", false); + + return; +} + +public Action CommandListener_SmChat(int client, const char[] sCommand, int argc) +{ + if (client <= 0) + return Plugin_Continue; + + char sText[256]; + char sUsername[32]; + + GetCmdArgString(sText, sizeof(sText)); + GetClientName(client, sUsername, sizeof(sUsername)); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_ADMINCHAT_WEBHOOKURL, sText, true, sUsername, true, g_sAvatarURL[client]); + else + Discord_POST(DISCORD_ADMINCHAT_WEBHOOKURL, sText, true, sUsername); + + return Plugin_Continue; +} + +public Action OnClientSayCommand(int client, const char[] sCommand, const char[] sArgs) +{ + if (client <= 0 || !IsClientInGame(client) || BaseComm_IsClientGagged(client)) + return Plugin_Continue; + + char sFinal[256]; + char sUsername[MAX_NAME_LENGTH]; + + GetClientName(client, sUsername, sizeof(sUsername)); + + if (strcmp(sCommand, "say_team") == 0) + { + if (sArgs[0] == '@') + { + bool bAdmin = CheckCommandAccess(client, "", ADMFLAG_GENERIC, true); + Format(sFinal, sizeof(sFinal), "%s%s", bAdmin ? "" : "To Admins: ", sArgs[1]); + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_ADMINCHAT_WEBHOOKURL, sFinal, true, sUsername, true, g_sAvatarURL[client]); + else + Discord_POST(DISCORD_ADMINCHAT_WEBHOOKURL, sFinal, true, sUsername); + + if (!bAdmin) + { + //g_iReplyTargetSerial = GetClientSerial(client); + //g_iReplyType = REPLYTYPE_CHAT; + } + + return Plugin_Continue; + } + + char sTeamName[32]; + + GetTeamName(GetClientTeam(client), sTeamName, sizeof(sTeamName)); + Format(sFinal, sizeof(sFinal), "(%s) ", sTeamName); + } + + return Plugin_Continue; +} + +public void CallAdmin_OnReportPost(int client, int target, const char[] reason) +{ + char sClientName[MAX_NAME_LENGTH]; + char sClientID[21]; + + char sTargetName[MAX_NAME_LENGTH]; + char sTargetID[21]; + + char sServerIP[16]; + int serverPort; + char sServerName[128]; + + CallAdmin_GetHostIP(sServerIP, sizeof(sServerIP)); + serverPort = CallAdmin_GetHostPort(); + CallAdmin_GetHostName(sServerName, sizeof(sServerName)); + + // Reporter wasn't a real client (initiated by a module) + if (client == REPORTER_CONSOLE) + { + strcopy(sClientName, sizeof(sClientName), "Server/Console"); + strcopy(sClientID, sizeof(sClientID), "Server/Console"); + } + else + { + GetClientName(client, sClientName, sizeof(sClientName)); + GetClientAuthId(client, AuthId_Steam2, sClientID, sizeof(sClientID)); + } + + GetClientName(target, sTargetName, sizeof(sTargetName)); + GetClientAuthId(target, AuthId_Steam2, sTargetID, sizeof(sTargetID)); + + g_iLastReportID = CallAdmin_GetReportID(); + + char currentMap[64]; + GetCurrentMap(currentMap, sizeof(currentMap)); + + char sMessage[4096]; + Format(sMessage, sizeof(sMessage), "@here\n```%s - Tick: %d``````New report on server: %s (%s:%d)\nReportID: %d\nReporter: %s (%s)\nTarget: %s (%s)\nReason: %s\nJoin Server: steam://connect/%s:%d\nwhen in game, type !calladmin_handle %d or /calladmin_handle %d in chat to handle this report```", currentMap, GetGameTickCount(), sServerName, sServerIP, serverPort, g_iLastReportID, sClientName, sClientID, sTargetName, sTargetID, reason, sServerIP, serverPort, g_iLastReportID, g_iLastReportID); + + char sUsername[MAX_NAME_LENGTH]; + GetClientName(client, sUsername, sizeof(sUsername)); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false); + else + Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, false, "", false); +} + +public void CallAdmin_OnReportHandled(int client, int id) +{ + if (id != g_iLastReportID) + { + return; + } + + char sMessage[1024]; + Format(sMessage, sizeof(sMessage), "```Last report (%d) was handled by: %N```", g_iLastReportID, client); + + char sUsername[MAX_NAME_LENGTH]; + GetClientName(client, sUsername, sizeof(sUsername)); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false); + else + Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, false, "", false); +} + +public void AntiBhopCheat_OnClientDetected(int client, char[] sReason, char[] sStats) +{ + char sUsername[MAX_NAME_LENGTH]; + GetClientName(client, sUsername, sizeof(sUsername)); + + char currentMap[64]; + GetCurrentMap(currentMap, sizeof(currentMap)); + + char sMessage[4096]; + Format(sMessage, sizeof(sMessage), "```%s - Tick: %d``````%s\n%s```", currentMap, GetGameTickCount(), sReason, sStats); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_ANTIBHOPCHEAT_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false); + else + Discord_POST(DISCORD_ANTIBHOPCHEAT_WEBHOOKURL, sMessage, true, sUsername, false, "", false); +} + +public int entWatch_OnClientBanned(int admin, int iLenght, int client) +{ + char sUsername[MAX_NAME_LENGTH]; + GetClientName(client, sUsername, sizeof(sUsername)); + + char currentMap[64]; + GetCurrentMap(currentMap, sizeof(currentMap)); + + char sMessageTmp[4096]; + + if (iLenght == 0) + { + Format(sMessageTmp, sizeof(sMessageTmp), "%L got temporarily restricted by %L", client, admin); + } + else if (iLenght == -1) + { + Format(sMessageTmp, sizeof(sMessageTmp), "%L got PERMANENTLY restricted by %L", client, admin); + } + else + { + Format(sMessageTmp, sizeof(sMessageTmp), "%L got restricted by %L for %d minutes", client, admin, iLenght); + } + + + char sMessage[4096]; + Format(sMessage, sizeof(sMessage), "```%s - Tick: %d``````%s```", currentMap, GetGameTickCount(), sMessageTmp); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false); + else + Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, false, "", false); +} + +public int entWatch_OnClientUnbanned(int admin, int client) +{ + char sUsername[MAX_NAME_LENGTH]; + GetClientName(client, sUsername, sizeof(sUsername)); + + char currentMap[64]; + GetCurrentMap(currentMap, sizeof(currentMap)); + + char sMessageTmp[4096]; + Format(sMessageTmp, sizeof(sMessageTmp), "%L got unrestricted by %L", client, admin); + + char sMessage[4096]; + Format(sMessage, sizeof(sMessage), "```%s - Tick: %d``````%s```", currentMap, GetGameTickCount(), sMessageTmp); + + if (g_sAvatarURL[client][0] != '\0') + Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false); + else + Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, false, "", false); +} \ No newline at end of file diff --git a/includes/AntiBhopCheat.inc b/includes/AntiBhopCheat.inc new file mode 100644 index 00000000..0b968fb1 --- /dev/null +++ b/includes/AntiBhopCheat.inc @@ -0,0 +1,6 @@ +#if defined _AntiBhopCheat_Included + #endinput +#endif +#define _AntiBhopCheat_Included + +forward void AntiBhopCheat_OnClientDetected(int client, char[] sReason, char[] sStats); \ No newline at end of file diff --git a/includes/calladmin.inc b/includes/calladmin.inc new file mode 100644 index 00000000..a6ca9b84 --- /dev/null +++ b/includes/calladmin.inc @@ -0,0 +1,289 @@ +/** Include guard */ +#if defined _calladmin_included + #endinput +#endif +#define _calladmin_included + + + + +/** Global calladmin version, do not change */ +#define CALLADMIN_VERSION "0.1.5" + + + +/** Pass this as a clientindex to the ReportPlayer native if you don't have a client, eg report from server automatically */ +#define REPORTER_CONSOLE 0 + + +/** Maximum size a reason string can be in length */ +#define REASON_MAX_LENGTH 128 + + + +/** + * Called when the main CallAdmin client selection menu is about to be drawn for a client. + * + * @param client Client index of the caller. + * @return Plugin_Continue to allow, Plugin_Handled otherwise. + */ +forward Action CallAdmin_OnDrawMenu(int client); + + + + +/** + * Called when the own reason selection is enabled and the select item for it is about to be drawn for a client. + * + * @param client Client index of the caller. + * @return Plugin_Continue to allow, Plugin_Handled otherwise. + */ +forward Action CallAdmin_OnDrawOwnReason(int client); + + + + +/** + * Called when a target is about to be drawn to the target selection menu for a client. + * Note: Called *n-1 times for the client selection menu where n is the amount of valid targets including the caller. + * + * @param client Client index of the caller. + * @param target Client index of the target about to be drawed. + * @return Plugin_Continue to allow the target to be drawn, Plugin_Handled otherwise. + */ +forward Action CallAdmin_OnDrawTarget(int client, int target); + + + + +/** + * Called when the trackercount was changed. + * + * @param oldVal Trackercount before update. + * @param newVal Trackercount after update. + * @noreturn + */ +forward void CallAdmin_OnTrackerCountChanged(int oldVal, int newVal); + + + + +/** + * Called before a client (or module) has reported another client. + * + * @param client Client index of the caller. + * @param target Client index of the target. + * @param reason Reason selected by the client for the report. + * @return Plugin_Continue to allow, Plugin_Handled otherwise. + */ +forward Action CallAdmin_OnReportPre(int client, int target, const char[] reason); + + + + +/** + * Called after a client (or module) has reported another client. + * Be sure to check that client != REPORTER_CONSOLE if you expect a valid client index. + * + * @param client Client index of the caller. + * @param target Client index of the target. + * @param reason Reason selected by the client for the report. + * @noreturn + */ +forward void CallAdmin_OnReportPost(int client, int target, const char[] reason); + + + + +/** + * Initiates an report call. + * If you report automatically (via a module for example) set the clientindex to REPORTER_CONSOLE. + * + * @param client Client index of the caller. + * @param target Client index of the target. + * @param reason Reason for the report. + * @return True if target could be reported, false otherwise. + */ +native bool CallAdmin_ReportClient(int client, int target, const char[] reason); + + + + +/** + * Called when an admin is about to be added to the in-game admin count. + * + * @param client Client index of the admin. + * @return Plugin_Continue to allow, Plugin_Handled otherwise. + */ +forward Action CallAdmin_OnAddToAdminCount(int client); + + + + +/** + * Returns the cached count of current trackers. + * + * @return Count of current trackers. + */ +native int CallAdmin_GetTrackersCount(); + + + + +/** + * Requests a forced refresh of the trackers count. + * Note that most modules work asynchronous and only return their own cached count. + * + * @noreturn + */ +native void CallAdmin_RequestTrackersCountRefresh(); + + + + +/** + * Called when the trackercount of a module is requested. + * This is either called periodically via the base calladmin, or when RequestTrackersCountRefresh is invoked. + * + * @param trackers By ref value of your trackers. + * @noreturn + */ +forward void CallAdmin_OnRequestTrackersCountRefresh(int &trackers); + + + + +enum ServerData +{ + ServerData_HostName, /**< This is the hostname of the server, gathered from the `hostname` convar */ + ServerData_HostIP, /**< This is the hostip of the server, gathered and converted from the `hostip` convar */ + ServerData_HostPort /**< This is the hostport of the server, gathered from the `hostport` convar */ +}; + + + + +/** + * Called when the serverdata data is changed. + * + * @param convar Handle to the convar which was changed. + * @param type Type of data which was changed. + * @param oldVal Value of data before change. + * @param newVal Value of data after change. + * @noreturn + */ +forward void CallAdmin_OnServerDataChanged(ConVar convar, ServerData type, const char[] oldVal, const char[] newVal); + + + + +/** + * Returns the server's hostname. + * + * @param buffer String to copy hostname to. + * @param max_size Maximum size of buffer. + * @noreturn + */ +native void CallAdmin_GetHostName(char[] buffer, int max_size); + + + + +/** + * Returns the server's IP String. + * + * @param buffer String to copy hostip to. + * @param max_size Maximum size of buffer. + * @noreturn + */ +native void CallAdmin_GetHostIP(char[] buffer, int max_size); + + + + +/** + * Returns the server's HostPort. + * + * @return Hostport. + */ +native int CallAdmin_GetHostPort(); + + + + +/** + * Logs a message to the calladmin logfile. + * The message has this format "[Pluginname] Message", where the plugin name is detected automatically. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +native void CallAdmin_LogMessage(const char[] format, any ...); + + + + +/** + * Called when a message was logged to the calladmin logfile. + * + * @param plugin Handle to the plugin which logged a message. + * @param message Message which was logged. + * @noreturn + */ +forward void CallAdmin_OnLogMessage(Handle plugin, const char[] message); + + + + +/** + * Returns the server's current report id. + * This is a temporary value and is increased with each report. + * + * @return Current report id of the server. + */ +native int CallAdmin_GetReportID(); + + + + +/** + * Called when a report was handled. + * + * @param client Admin who handled the report. + * @param id ID of the handled report. + * @noreturn + */ +forward void CallAdmin_OnReportHandled(int client, int id); + + + + +/* Do not edit below this line */ +public SharedPlugin __pl_calladmin = +{ + name = "calladmin", + file = "calladmin.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + + + + +#if !defined REQUIRE_PLUGIN +public __pl_calladmin_SetNTVOptional() +{ + MarkNativeAsOptional("CallAdmin_GetTrackersCount"); + MarkNativeAsOptional("CallAdmin_RequestTrackersCountRefresh"); + MarkNativeAsOptional("CallAdmin_GetHostName"); + MarkNativeAsOptional("CallAdmin_GetHostIP"); + MarkNativeAsOptional("CallAdmin_GetHostPort"); + MarkNativeAsOptional("CallAdmin_ReportClient"); + MarkNativeAsOptional("CallAdmin_LogMessage"); + MarkNativeAsOptional("CallAdmin_GetReportID"); +} +#endif