├── scripting ├── include │ ├── smjansson.inc │ ├── servermanager.inc │ ├── discord │ │ ├── GuildMember.inc │ │ ├── channel.inc │ │ ├── user.inc │ │ ├── stocks.inc │ │ ├── webhook.inc │ │ ├── message_embed.inc │ │ ├── message.inc │ │ └── bot.inc │ ├── chat-processor.inc │ ├── sourcebanspp.inc │ ├── discord.inc │ ├── sourcecomms.inc │ ├── SteamWorks.inc │ └── colorvariables.inc ├── discord │ ├── MessageObject.sp │ ├── UserObject.sp │ ├── GuildRole.sp │ ├── DiscordRequest.sp │ ├── SendWebHook.sp │ ├── deletemessage.sp │ ├── SendMessage.sp │ ├── GetGuildChannels.sp │ ├── ListenToChannel.sp │ ├── GetGuilds.sp │ ├── GuildMembers.sp │ └── reactions.sp ├── discord_manager_natives.sp └── discord_api.sp ├── configs └── dsm │ └── command_listener.ini ├── README.md └── translations ├── pt └── dsm.phrases.txt └── dsm.phrases.txt /scripting/include/smjansson.inc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KillStr3aK/discord-server-manager/HEAD/scripting/include/smjansson.inc -------------------------------------------------------------------------------- /configs/dsm/command_listener.ini: -------------------------------------------------------------------------------- 1 | "Command_Listener" 2 | { 3 | "res"{} 4 | "rs"{} 5 | "sm_vip"{} 6 | "sm_ws"{} 7 | "sm_gloves"{} 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # discord-server-manager 2 | ## BEFORE ASKING HOW-TO QUESTIONS PLEASE TAKE A LOOK ON THE WIKI. IF YOU'RE STILL LOST JOIN TO THIS DISCORD SO YOU CAN ASK FOR HELP: 3 | ## https://discord.gg/MHdYFby 4 | Discord Server Manager with features like verification system and chat-relay, map change, ban, comms, report notifications 5 | -------------------------------------------------------------------------------- /scripting/include/servermanager.inc: -------------------------------------------------------------------------------- 1 | #if defined _servermanager_included 2 | #endinput 3 | #endif 4 | #define _servermanager_included 5 | 6 | forward void DSM_OnLinkedAccount(int client, const char[] userid, const char[] username, const char[] discriminator); 7 | 8 | native void DSM_RefreshClients(); 9 | native bool DSM_IsMember(int client); 10 | native char DSM_GetUserId(int client, char[] output, int maxsize); 11 | 12 | public SharedPlugin __pl_servermanager = { 13 | name = "discord_manager", 14 | file = "discord_manager.smx", 15 | #if defined REQUIRE_PLUGIN 16 | required = 1, 17 | #else 18 | required = 0, 19 | #endif 20 | }; 21 | 22 | #if !defined REQUIRE_PLUGIN 23 | public void __pl_servermanager_SetNTVOptional() 24 | { 25 | MarkNativeAsOptional("DSM_IsMember"); 26 | MarkNativeAsOptional("DSM_GetUserId"); 27 | MarkNativeAsOptional("DSM_RefreshClients"); 28 | } 29 | #endif -------------------------------------------------------------------------------- /scripting/include/discord/GuildMember.inc: -------------------------------------------------------------------------------- 1 | methodmap DiscordGuildUser < Handle { 2 | //Returns User Object 3 | public DiscordUser GetUser() { 4 | return view_as(json_object_get(this, "user")); 5 | } 6 | 7 | //Returns player's nick 8 | public void GetNickname(char[] buffer, int maxlength) { 9 | JsonObjectGetString(this, "nick", buffer, maxlength); 10 | } 11 | 12 | //Returns JSON array list of roles. You can manually loop through them for now. 13 | public Handle GetRoles() { 14 | return json_object_get(this, "roles"); 15 | } 16 | 17 | //Returns the date the user joined the guild in format: "2015-04-26T06:26:56.936000+00:00" 18 | public void GetJoinedAt(char[] buffer, int maxlength) { 19 | JsonObjectGetString(this, "joined_at", buffer, maxlength); 20 | } 21 | 22 | public bool IsDeaf() { 23 | return JsonObjectGetBool(this, "deaf"); 24 | } 25 | 26 | public bool IsMute() { 27 | return JsonObjectGetBool(this, "mute"); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /scripting/discord/MessageObject.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordMessage_GetID(Handle plugin, int numParams) { 2 | Handle hJson = GetNativeCell(1); 3 | char buffer[64]; 4 | JsonObjectGetString(hJson, "id", buffer, sizeof(buffer)); 5 | SetNativeString(2, buffer, GetNativeCell(3)); 6 | } 7 | 8 | public int Native_DiscordMessage_IsPinned(Handle plugin, int numParams) { 9 | Handle hJson = GetNativeCell(1); 10 | return JsonObjectGetBool(hJson, "pinned"); 11 | } 12 | 13 | public int Native_DiscordMessage_GetAuthor(Handle plugin, int numParams) { 14 | Handle hJson = GetNativeCell(1); 15 | 16 | Handle hAuthor = json_object_get(hJson, "author"); 17 | 18 | DiscordUser user = view_as(CloneHandle(hAuthor, plugin)); 19 | delete hAuthor; 20 | 21 | return _:user; 22 | } 23 | 24 | public int Native_DiscordMessage_GetContent(Handle plugin, int numParams) { 25 | Handle hJson = GetNativeCell(1); 26 | static char buffer[2000]; 27 | JsonObjectGetString(hJson, "content", buffer, sizeof(buffer)); 28 | SetNativeString(2, buffer, GetNativeCell(3)); 29 | } 30 | 31 | public int Native_DiscordMessage_GetChannelID(Handle plugin, int numParams) { 32 | Handle hJson = GetNativeCell(1); 33 | char buffer[64]; 34 | JsonObjectGetString(hJson, "channel_id", buffer, sizeof(buffer)); 35 | SetNativeString(2, buffer, GetNativeCell(3)); 36 | } -------------------------------------------------------------------------------- /scripting/discord_manager_natives.sp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define PLUGIN_NEV "DSM NATIVES" 5 | #define PLUGIN_LERIAS "EXAMPLES" 6 | #define PLUGIN_AUTHOR "Nexd" 7 | #define PLUGIN_VERSION "1.0" 8 | #define PLUGIN_URL "https://github.com/KillStr3aK" 9 | #pragma tabsize 0; 10 | #pragma newdecls required; 11 | #pragma semicolon 1; 12 | 13 | public Plugin myinfo = 14 | { 15 | name = PLUGIN_NEV, 16 | author = PLUGIN_AUTHOR, 17 | description = PLUGIN_LERIAS, 18 | version = PLUGIN_VERSION, 19 | url = PLUGIN_URL 20 | }; 21 | 22 | public void OnPluginStart() 23 | { 24 | RegConsoleCmd("sm_member", Command_Member); 25 | } 26 | 27 | public Action Command_Member(int client, int args) 28 | { 29 | if(DSM_IsMember(client)) 30 | { 31 | char userid[64]; 32 | DSM_GetUserId(client, userid, sizeof(userid)); 33 | PrintToChat(client, " \x04You are a member! Userid: %s", userid); 34 | } else { 35 | PrintToChat(client, " \x07You aren't a member!"); 36 | } 37 | 38 | DSM_RefreshClients(); //Refresh every client (Call OnClientPostAdminCheck) 39 | 40 | return Plugin_Handled; 41 | } 42 | 43 | public void DSM_OnLinkedAccount(int client, const char[] userid, const char[] username, const char[] discriminator) 44 | { 45 | PrintToChatAll("%N %s%s (%s)", client, username, discriminator, userid); 46 | } 47 | -------------------------------------------------------------------------------- /scripting/discord/UserObject.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordUser_GetID(Handle plugin, int numParams) { 2 | Handle hJson = GetNativeCell(1); 3 | char buffer[64]; 4 | JsonObjectGetString(hJson, "id", buffer, sizeof(buffer)); 5 | SetNativeString(2, buffer, GetNativeCell(3)); 6 | } 7 | 8 | public int Native_DiscordUser_GetUsername(Handle plugin, int numParams) { 9 | Handle hJson = GetNativeCell(1); 10 | char buffer[64]; 11 | JsonObjectGetString(hJson, "username", buffer, sizeof(buffer)); 12 | SetNativeString(2, buffer, GetNativeCell(3)); 13 | } 14 | 15 | public int Native_DiscordUser_GetDiscriminator(Handle plugin, int numParams) { 16 | Handle hJson = GetNativeCell(1); 17 | char buffer[16]; 18 | JsonObjectGetString(hJson, "discriminator", buffer, sizeof(buffer)); 19 | SetNativeString(2, buffer, GetNativeCell(3)); 20 | } 21 | 22 | public int Native_DiscordUser_GetAvatar(Handle plugin, int numParams) { 23 | Handle hJson = GetNativeCell(1); 24 | char buffer[256]; 25 | JsonObjectGetString(hJson, "avatar", buffer, sizeof(buffer)); 26 | SetNativeString(2, buffer, GetNativeCell(3)); 27 | } 28 | 29 | public int Native_DiscordUser_GetEmail(Handle plugin, int numParams) { 30 | Handle hJson = GetNativeCell(1); 31 | char buffer[64]; 32 | JsonObjectGetString(hJson, "email", buffer, sizeof(buffer)); 33 | SetNativeString(2, buffer, GetNativeCell(3)); 34 | } 35 | 36 | public int Native_DiscordUser_IsVerified(Handle plugin, int numParams) { 37 | Handle hJson = GetNativeCell(1); 38 | return JsonObjectGetBool(hJson, "verified"); 39 | } 40 | 41 | public int Native_DiscordUser_IsBot(Handle plugin, int numParams) { 42 | Handle hJson = GetNativeCell(1); 43 | return JsonObjectGetBool(hJson, "bot"); 44 | } -------------------------------------------------------------------------------- /scripting/include/discord/channel.inc: -------------------------------------------------------------------------------- 1 | enum 2 | { 3 | GUILD_TEXT = 0, 4 | DM, 5 | GUILD_VOICE, 6 | GROUP_DM, 7 | GUILD_CATEGORY 8 | }; 9 | 10 | methodmap DiscordChannel < StringMap { 11 | public DiscordChannel() { 12 | Handle hObj = json_object(); 13 | return view_as(hObj); 14 | } 15 | 16 | public native void SendMessage(DiscordBot Bot, char[] message, OnMessageSent fCallback=INVALID_FUNCTION, any data=0); 17 | 18 | public void GetGuildID(char[] buffer, int maxlength) { 19 | JsonObjectGetString(this, "guild_id", buffer, maxlength); 20 | } 21 | 22 | public void GetID(char[] buffer, int maxlength) { 23 | JsonObjectGetString(this, "id", buffer, maxlength); 24 | } 25 | 26 | public void GetName(char[] buffer, int maxlength) { 27 | JsonObjectGetString(this, "name", buffer, maxlength); 28 | } 29 | 30 | property int Position { 31 | public get() { 32 | return JsonObjectGetInt(this, "position"); 33 | } 34 | } 35 | 36 | property bool IsPrivate { 37 | public get() { 38 | return JsonObjectGetBool(this, "is_private"); 39 | } 40 | } 41 | 42 | public void GetTopic(char[] buffer, int maxlength) { 43 | JsonObjectGetString(this, "topic", buffer, maxlength); 44 | } 45 | 46 | public void GetLastMessageID(char[] buffer, int maxlength) { 47 | JsonObjectGetString(this, "last_message_id", buffer, maxlength); 48 | } 49 | 50 | public void SetLastMessageID(const char[] id) { 51 | json_object_set_new(this, "last_message_id", json_string(id)); 52 | } 53 | 54 | property int Type { 55 | public get() { 56 | return JsonObjectGetInt(this, "type"); 57 | } 58 | } 59 | 60 | property int Bitrate { 61 | public get() { 62 | return JsonObjectGetInt(this, "bitrate"); 63 | } 64 | } 65 | 66 | property int UserLimit { 67 | public get() { 68 | return JsonObjectGetInt(this, "user_limit"); 69 | } 70 | } 71 | 72 | property bool IsText { 73 | public get() { 74 | return this.Type == GUILD_TEXT; 75 | } 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /scripting/include/discord/user.inc: -------------------------------------------------------------------------------- 1 | methodmap DiscordUser < JSON_Object { 2 | public bool GetID(char[] buffer, int maxlength) { 3 | return JsonObjectGetString(this, "id", buffer, maxlength); 4 | } 5 | 6 | public bool GetUsername(char[] buffer, int maxlength) { 7 | return JsonObjectGetString(this, "username", buffer, maxlength); 8 | } 9 | 10 | public bool GetDiscriminator(char[] buffer, int maxlength) { 11 | return JsonObjectGetString(this, "discriminator", buffer, maxlength); 12 | } 13 | public int GetDiscriminatorInt() { 14 | char buffer[16]; 15 | this.GetDiscriminator(buffer, sizeof(buffer)); 16 | return StringToInt(buffer); 17 | } 18 | 19 | public bool GetAvatar(char[] buffer, int maxlength) { 20 | return JsonObjectGetString(this, "avatar", buffer, maxlength); 21 | } 22 | 23 | public bool IsVerified() { 24 | return JsonObjectGetBool(this, "verified"); 25 | } 26 | 27 | public bool GetEmail(char[] buffer, int maxlength) { 28 | return JsonObjectGetString(this, "email", buffer, maxlength); 29 | } 30 | 31 | public bool IsBot() { 32 | return JsonObjectGetBool(this, "bot"); 33 | } 34 | }; 35 | 36 | methodmap DiscordGuildUser < DiscordUser { 37 | //Returns User Object 38 | property DiscordUser AsUser { 39 | public get() { return view_as(this); } 40 | } 41 | 42 | //Returns player's nick 43 | public void GetNickname(char[] buffer, int maxlength) { 44 | JsonObjectGetString(this, "nick", buffer, maxlength); 45 | } 46 | 47 | //Returns JSON array list of roles. You can manually loop through them for now. 48 | public JSON_Object GetRoles() { 49 | return this.GetObject("roles"); 50 | } 51 | 52 | //Returns the date the user joined the guild in format: "2015-04-26T06:26:56.936000+00:00" 53 | public void GetJoinedAt(char[] buffer, int maxlength) { 54 | JsonObjectGetString(this, "joined_at", buffer, maxlength); 55 | } 56 | 57 | public bool IsDeaf() { 58 | return JsonObjectGetBool(this, "deaf"); 59 | } 60 | 61 | public bool IsMute() { 62 | return JsonObjectGetBool(this, "mute"); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /translations/pt/dsm.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Manager.YourID" 4 | { 5 | "pt" "Seu ID: {grey}{1}" 6 | } 7 | 8 | "Manager.Connect" 9 | { 10 | "pt" "Conecte-se ao nosso servidor de {darkred}Discord: {blue}{1}" 11 | } 12 | 13 | "Manager.Usage" 14 | { 15 | "pt" "Use {yellow}{1} {2}{default} no canal {purple}#{3} {default}para ser verificado!" 16 | } 17 | 18 | "Manager.MustVerify" 19 | { 20 | "pt" "Você precisa verificar sua conta {blue}Discord{default}! Digite {gold}{1}" 21 | } 22 | 23 | "Manager.AlreadyLinked" 24 | { 25 | "pt" "<@{1}> Esta conta de discord já está vinculada! ❌" 26 | } 27 | 28 | "Manager.Linked" 29 | { 30 | "pt" "<@{1}> Você vinculou sua conta! ✅" 31 | } 32 | 33 | "Manager.Verified" 34 | { 35 | "pt" "Você verificou sua conta. ({green}{1}#{2}{default})" 36 | } 37 | 38 | "Manager.Info" 39 | { 40 | "pt" "<@{1}> Este canal está sendo usado para vincular contas!\Digite: {2} " 41 | } 42 | 43 | "Manager.Invalid" 44 | { 45 | "pt" "<@{1}> Este código é inválido!" 46 | } 47 | 48 | "Modules.WebHook.LengthTime" 49 | { 50 | "pt" "{1} minutos" 51 | } 52 | 53 | "Modules.WebHook.LengthTemp" 54 | { 55 | "pt" "TEMPORÁRIO" 56 | } 57 | 58 | "Modules.WebHook.LengthPerm" 59 | { 60 | "pt" "PERMANENTE" 61 | } 62 | 63 | "Modules.WebHook.AdminField" 64 | { 65 | "pt" "ADMIN" 66 | } 67 | 68 | "Modules.WebHook.TargetField" 69 | { 70 | "pt" "ALVO" 71 | } 72 | 73 | "Modules.WebHook.LengthField" 74 | { 75 | "pt" "COMPRIMENTO" 76 | } 77 | 78 | "Modules.WebHook.TypeField" 79 | { 80 | "pt" "TIPO" 81 | } 82 | 83 | "Modules.WebHook.ReasonField" 84 | { 85 | "pt" "MOTIVO" 86 | } 87 | 88 | "Modules.WebHook.CurrentMapField" 89 | { 90 | "pt" "Mapa Atual:" 91 | } 92 | 93 | "Modules.WebHook.PlayersOnlineField" 94 | { 95 | "pt" "Players Online:" 96 | } 97 | 98 | "Modules.WebHook.DirectConnectField" 99 | { 100 | "pt" "Direct Connect:" 101 | } 102 | 103 | "Modules.ChatRelay.InvalidName" 104 | { 105 | "pt" "NOME INVÁLIDO" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /scripting/include/discord/stocks.inc: -------------------------------------------------------------------------------- 1 | stock int JsonObjectGetInt(Handle hElement, char[] key) { 2 | Handle hObject = json_object_get(hElement, key); 3 | if(hObject == INVALID_HANDLE) return 0; 4 | 5 | int value; 6 | if(json_is_integer(hObject)) { 7 | value = json_integer_value(hObject); 8 | }else if(json_is_string(hObject)) { 9 | char buffer[12]; 10 | json_string_value(hObject, buffer, sizeof(buffer)); 11 | value = StringToInt(buffer); 12 | } 13 | CloseHandle(hObject); 14 | return value; 15 | } 16 | 17 | stock bool JsonObjectGetString(Handle hElement, char[] key, char[] buffer, maxlength) { 18 | Handle hObject = json_object_get(hElement, key); 19 | if(hObject == INVALID_HANDLE) return false; 20 | 21 | if(json_is_integer(hObject)) { 22 | IntToString(json_integer_value(hObject), buffer, maxlength); 23 | }else if(json_is_string(hObject)) { 24 | json_string_value(hObject, buffer, maxlength); 25 | }else if(json_is_real(hObject)) { 26 | FloatToString(json_real_value(hObject), buffer, maxlength); 27 | }else if(json_is_true(hObject)) { 28 | FormatEx(buffer, maxlength, "true"); 29 | }else if(json_is_false(hObject)) { 30 | FormatEx(buffer, maxlength, "false"); 31 | } 32 | CloseHandle(hObject); 33 | return true; 34 | } 35 | 36 | stock bool JsonObjectGetBool(Handle hElement, char[] key, bool defaultvalue=false) { 37 | Handle hObject = json_object_get(hElement, key); 38 | if(hObject == INVALID_HANDLE) return defaultvalue; 39 | 40 | bool ObjectBool = defaultvalue; 41 | 42 | if(json_is_integer(hObject)) { 43 | ObjectBool = view_as(json_integer_value(hObject)); 44 | }else if(json_is_string(hObject)) { 45 | char buffer[11]; 46 | json_string_value(hObject, buffer, sizeof(buffer)); 47 | if(StrEqual(buffer, "true", false)) { 48 | ObjectBool = true; 49 | }else if(StrEqual(buffer, "false", false)) { 50 | ObjectBool = false; 51 | }else { 52 | int x = StringToInt(buffer); 53 | ObjectBool = view_as(x); 54 | } 55 | }else if(json_is_real(hObject)) { 56 | ObjectBool = view_as(RoundToFloor(json_real_value(hObject))); 57 | }else if(json_is_true(hObject)) { 58 | ObjectBool = true; 59 | }else if(json_is_false(hObject)) { 60 | ObjectBool = false; 61 | } 62 | CloseHandle(hObject); 63 | return ObjectBool; 64 | } 65 | 66 | stock float JsonObjectGetFloat(Handle hJson, char[] key, float defaultValue=0.0) { 67 | Handle hObject = json_object_get(hJson, key); 68 | if(hObject == INVALID_HANDLE) return defaultValue; 69 | 70 | float value = defaultValue; 71 | if(json_is_integer(hObject)) { 72 | value = float(json_integer_value(hObject)); 73 | }else if(json_is_real(hObject)) { 74 | value = json_real_value(hObject); 75 | }else if(json_is_string(hObject)) { 76 | char buffer[12]; 77 | json_string_value(hObject, buffer, sizeof(buffer)); 78 | value = StringToFloat(buffer); 79 | } 80 | CloseHandle(hObject); 81 | return value; 82 | } 83 | -------------------------------------------------------------------------------- /scripting/include/chat-processor.inc: -------------------------------------------------------------------------------- 1 | #if defined _chat_processor_included 2 | #endinput 3 | #endif 4 | #define _chat_processor_included 5 | 6 | //Globals 7 | #define MAXLENGTH_FLAG 32 8 | #define MAXLENGTH_NAME 128 9 | #define MAXLENGTH_MESSAGE 128 10 | #define MAXLENGTH_BUFFER 255 11 | 12 | //Natives 13 | /** 14 | * Retrieves the current format string assigned from a flag string. 15 | * Example: "Cstrike_Chat_All" = "{1} : {2}" 16 | * You can find the config formats in either the translations or the configs. 17 | * 18 | * param sFlag Flag string to retrieve the format string from. 19 | * param sBuffer Format string from the flag string. 20 | * param iSize Size of the format string buffer. 21 | * 22 | * noreturn 23 | **/ 24 | native void ChatProcessor_GetFlagFormatString(const char[] sFlag, char[] sBuffer, int iSize); 25 | 26 | //Forwards 27 | /** 28 | * Called while sending a chat message before It's sent. 29 | * Limits on the name and message strings can be found above. 30 | * 31 | * param author Author that created the message. 32 | * param recipients Array of clients who will receive the message. 33 | * param flagstring Flag string to determine the type of message. 34 | * param name Name string of the author to be pushed. 35 | * param message Message string from the author to be pushed. 36 | * param processcolors Toggle to process colors in the buffer strings. 37 | * param removecolors Toggle to remove colors in the buffer strings. (Requires bProcessColors = true) 38 | * 39 | * return types 40 | * - Plugin_Continue Stops the message. 41 | * - Plugin_Stop Stops the message. 42 | * - Plugin_Changed Fires the post-forward below and prints out a message. 43 | * - Plugin_Handled Fires the post-forward below but doesn't print a message. 44 | **/ 45 | forward Action CP_OnChatMessage(int& author, ArrayList recipients, char[] flagstring, char[] name, char[] message, bool& processcolors, bool& removecolors); 46 | 47 | /** 48 | * Called after the chat message is sent to the designated clients by the author. 49 | * 50 | * param author Author that sent the message. 51 | * param recipients Array of clients who received the message. 52 | * param flagstring Flag string to determine the type of message. 53 | * param formatstring Format string used in the message based on the flag string. 54 | * param name Name string of the author. 55 | * param message Message string from the author. 56 | * param processcolors Check if colors were processed in the buffer strings. 57 | * param removecolors Check if colors were removed from the buffer strings. 58 | * 59 | * noreturn 60 | **/ 61 | forward void CP_OnChatMessagePost(int author, ArrayList recipients, const char[] flagstring, const char[] formatstring, const char[] name, const char[] message, bool processcolors, bool removecolors); 62 | 63 | #if !defined REQUIRE_PLUGIN 64 | public void __pl_chat_processor_SetNTVOptional() 65 | { 66 | MarkNativeAsOptional("ChatProcessor_GetFlagFormatString"); 67 | } 68 | #endif 69 | 70 | public SharedPlugin __pl_chat_processor = 71 | { 72 | name = "chat-processor", 73 | file = "chat-processor.smx", 74 | #if defined REQUIRE_PLUGIN 75 | required = 1 76 | #else 77 | required = 0 78 | #endif 79 | }; -------------------------------------------------------------------------------- /scripting/discord/GuildRole.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_GetGuildRoles(Handle plugin, int numParams) { 2 | DiscordBot bot = view_as(CloneHandle(GetNativeCell(1))); 3 | 4 | char guild[32]; 5 | GetNativeString(2, guild, sizeof(guild)); 6 | 7 | Function fCallback = GetNativeCell(3); 8 | 9 | any data = GetNativeCell(4); 10 | 11 | Handle hData = json_object(); 12 | json_object_set_new(hData, "bot", bot); 13 | json_object_set_new(hData, "guild", json_string(guild)); 14 | json_object_set_new(hData, "data1", json_integer(view_as(data))); 15 | 16 | Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell); 17 | AddToForward(fwd, plugin, fCallback); 18 | json_object_set_new(hData, "callback", json_integer(view_as(fwd))); 19 | 20 | GetGuildRoles(hData); 21 | } 22 | 23 | static void GetGuildRoles(Handle hData) { 24 | DiscordBot bot = view_as(json_object_get(hData, "bot")); 25 | 26 | char guild[32]; 27 | JsonObjectGetString(hData, "guild", guild, sizeof(guild)); 28 | 29 | 30 | char url[256]; 31 | FormatEx(url, sizeof(url), "https://discordapp.com/api/guilds/%s/roles", guild); 32 | 33 | char route[128]; 34 | FormatEx(route, sizeof(route), "guild/%s/roles", guild); 35 | 36 | DiscordRequest request = new DiscordRequest(url, k_EHTTPMethodGET); 37 | if(request == null) { 38 | delete bot; 39 | CreateTimer(2.0, SendGetGuildRoles, hData); 40 | return; 41 | } 42 | request.SetCallbacks(HTTPCompleted, GetGuildRolesReceive); 43 | request.SetBot(bot); 44 | request.SetData(hData, route); 45 | 46 | request.Send(route); 47 | 48 | delete bot; 49 | } 50 | 51 | public Action SendGetGuildRoles(Handle timer, any data) { 52 | GetGuildRoles(view_as(data)); 53 | } 54 | 55 | 56 | public GetGuildRolesReceive(Handle request, bool failure, int offset, int statuscode, any dp) { 57 | if(failure || (statuscode != 200)) { 58 | if(statuscode == 400) { 59 | PrintToServer("BAD REQUEST"); 60 | } 61 | 62 | if(statuscode == 429 || statuscode == 500) { 63 | GetGuildRoles(dp); 64 | 65 | delete request; 66 | return; 67 | } 68 | LogError("[DISCORD] Couldn't Send GetGuildRoles - Fail %i %i", failure, statuscode); 69 | delete request; 70 | delete view_as(dp); 71 | return; 72 | } 73 | SteamWorks_GetHTTPResponseBodyCallback(request, GetRolesData, dp); 74 | delete request; 75 | } 76 | 77 | public int GetRolesData(const char[] data, any dp) { 78 | Handle hJson = json_load(data); 79 | Handle hData = view_as(dp); 80 | DiscordBot bot = view_as(json_object_get(hData, "bot")); 81 | 82 | Handle fwd = view_as(JsonObjectGetInt(hData, "callback")); 83 | 84 | char guild[32]; 85 | JsonObjectGetString(hData, "guild", guild, sizeof(guild)); 86 | 87 | any data1 = JsonObjectGetInt(hData, "data1"); 88 | 89 | if(fwd != null) { 90 | Call_StartForward(fwd); 91 | Call_PushCell(bot); 92 | Call_PushString(guild); 93 | Call_PushCell(view_as(hJson)); 94 | Call_PushCell(data1); 95 | Call_Finish(); 96 | } 97 | 98 | delete bot; 99 | delete hJson; 100 | delete hData; 101 | delete fwd; 102 | } -------------------------------------------------------------------------------- /scripting/discord/DiscordRequest.sp: -------------------------------------------------------------------------------- 1 | /* 2 | stock Handle PrepareRequest(DiscordBot bot, char[] url, EHTTPMethod method=k_EHTTPMethodGET, Handle hJson=null, SteamWorksHTTPDataReceived DataReceived = INVALID_FUNCTION, SteamWorksHTTPRequestCompleted RequestCompleted = INVALID_FUNCTION) { 3 | static char stringJson[16384]; 4 | stringJson[0] = '\0'; 5 | if(hJson != null) { 6 | json_dump(hJson, stringJson, sizeof(stringJson), 0, true); 7 | } 8 | 9 | //Format url 10 | static char turl[128]; 11 | FormatEx(turl, sizeof(turl), "https://discordapp.com/api/%s", url); 12 | 13 | Handle request = SteamWorks_CreateHTTPRequest(method, turl); 14 | if(request == null) { 15 | return null; 16 | } 17 | 18 | if(bot != null) { 19 | BuildAuthHeader(request, bot); 20 | } 21 | 22 | SteamWorks_SetHTTPRequestRawPostBody(request, "application/json; charset=UTF-8", stringJson, strlen(stringJson)); 23 | 24 | SteamWorks_SetHTTPRequestNetworkActivityTimeout(request, 30); 25 | 26 | if(RequestCompleted == INVALID_FUNCTION) { 27 | //I had some bugs previously where it wouldn't send request and return code 0 if I didn't set request completed. 28 | //This is just a safety then, my issue could have been something else and I will test more later on 29 | RequestCompleted = HTTPCompleted; 30 | } 31 | 32 | if(DataReceived == INVALID_FUNCTION) { 33 | //Need to close the request handle 34 | DataReceived = HTTPDataReceive; 35 | } 36 | 37 | SteamWorks_SetHTTPCallbacks(request, RequestCompleted, HeadersReceived, DataReceived); 38 | if(hJson != null) delete hJson; 39 | 40 | return request; 41 | } 42 | */ 43 | 44 | methodmap DiscordRequest < Handle { 45 | public DiscordRequest(char[] url, EHTTPMethod method) { 46 | Handle request = SteamWorks_CreateHTTPRequest(method, url); 47 | return view_as(request); 48 | } 49 | 50 | public void SetJsonBody(Handle hJson) { 51 | static char stringJson[16384]; 52 | stringJson[0] = '\0'; 53 | if(hJson != null) { 54 | json_dump(hJson, stringJson, sizeof(stringJson), 0, true); 55 | } 56 | SteamWorks_SetHTTPRequestRawPostBody(this, "application/json; charset=UTF-8", stringJson, strlen(stringJson)); 57 | if(hJson != null) delete hJson; 58 | } 59 | 60 | public void SetJsonBodyEx(Handle hJson) { 61 | static char stringJson[16384]; 62 | stringJson[0] = '\0'; 63 | if(hJson != null) { 64 | json_dump(hJson, stringJson, sizeof(stringJson), 0, true); 65 | } 66 | SteamWorks_SetHTTPRequestRawPostBody(this, "application/json; charset=UTF-8", stringJson, strlen(stringJson)); 67 | } 68 | 69 | property int Timeout { 70 | public set(int timeout) { 71 | SteamWorks_SetHTTPRequestNetworkActivityTimeout(this, timeout); 72 | } 73 | } 74 | 75 | public void SetCallbacks(SteamWorksHTTPRequestCompleted OnComplete, SteamWorksHTTPDataReceived DataReceived) { 76 | SteamWorks_SetHTTPCallbacks(this, OnComplete, HeadersReceived, DataReceived); 77 | } 78 | 79 | public void SetContextValue(any data1, any data2) { 80 | SteamWorks_SetHTTPRequestContextValue(this, data1, data2); 81 | } 82 | 83 | public void SetData(any data1, char[] route) { 84 | SteamWorks_SetHTTPRequestContextValue(this, data1, UrlToDP(route)); 85 | } 86 | 87 | public void SetBot(DiscordBot bot) { 88 | BuildAuthHeader(this, bot); 89 | } 90 | 91 | public void Send(char[] route) { 92 | DiscordSendRequest(this, route); 93 | } 94 | } -------------------------------------------------------------------------------- /scripting/discord/SendWebHook.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordWebHook_Send(Handle plugin, int numParams) { 2 | DiscordWebHook hook = GetNativeCell(1); 3 | SendWebHook(view_as(hook)); 4 | } 5 | 6 | public void SendWebHook(DiscordWebHook hook) { 7 | if(!JsonObjectGetBool(hook, "__selfCopy", false)) { 8 | hook = view_as(json_deep_copy(hook)); 9 | json_object_set_new(hook, "__selfCopy", json_true()); 10 | } 11 | Handle hJson = hook.Data; 12 | 13 | char url[256]; 14 | hook.GetUrl(url, sizeof(url)); 15 | 16 | if(hook.SlackMode) { 17 | if(StrContains(url, "/slack") == -1) { 18 | Format(url, sizeof(url), "%s/slack", url); 19 | } 20 | 21 | RenameJsonObject(hJson, "content", "text"); 22 | RenameJsonObject(hJson, "embeds", "attachments"); 23 | 24 | Handle hAttachments = json_object_get(hJson, "attachments"); 25 | if(hAttachments != null) { 26 | if(json_is_array(hAttachments)) { 27 | for(int i = 0; i < json_array_size(hAttachments); i++) { 28 | Handle hEmbed = json_array_get(hAttachments, i); 29 | 30 | Handle hFields = json_object_get(hEmbed, "fields"); 31 | if(hFields) { 32 | if(json_is_array(hFields)) { 33 | for(int j = 0; j < json_array_size(hFields); j++) { 34 | Handle hField = json_array_get(hFields, j); 35 | RenameJsonObject(hField, "name", "title"); 36 | RenameJsonObject(hField, "inline", "short"); 37 | //json_array_set_new(hFields, j, hField); 38 | delete hField; 39 | } 40 | } 41 | 42 | //json_object_set_new(hEmbed, "fields", hFields); 43 | delete hFields; 44 | } 45 | 46 | //json_array_set_new(hAttachments, i, hEmbed); 47 | delete hEmbed; 48 | } 49 | } 50 | 51 | //json_object_set_new(hJson, "attachments", hAttachments); 52 | delete hAttachments; 53 | } 54 | } 55 | 56 | //Send 57 | DiscordRequest request = new DiscordRequest(url, k_EHTTPMethodPOST); 58 | request.SetCallbacks(HTTPCompleted, SendWebHookReceiveData); 59 | request.SetJsonBodyEx(hJson); 60 | //Handle request = PrepareRequestRaw(null, url, k_EHTTPMethodPOST, hJson, SendWebHookReceiveData); 61 | if(request == null) { 62 | CreateTimer(2.0, SendWebHookDelayed, hJson); 63 | return; 64 | } 65 | 66 | request.SetContextValue(hJson, UrlToDP(url)); 67 | 68 | //DiscordSendRequest(request, url); 69 | request.Send(url); 70 | } 71 | 72 | public Action SendWebHookDelayed(Handle timer, any data) { 73 | SendWebHook(view_as(data)); 74 | } 75 | 76 | public SendWebHookReceiveData(Handle request, bool failure, int offset, int statuscode, any dp) { 77 | if(failure || (statuscode != 200 && statuscode != 204)) { 78 | if(statuscode == 400) { 79 | PrintToServer("BAD REQUEST"); 80 | SteamWorks_GetHTTPResponseBodyCallback(request, WebHookData, dp); 81 | } 82 | 83 | if(statuscode == 429 || statuscode == 500) { 84 | SendWebHook(view_as(dp)); 85 | 86 | delete request; 87 | return; 88 | } 89 | LogError("[DISCORD] Couldn't Send Webhook - Fail %i %i", failure, statuscode); 90 | delete request; 91 | delete view_as(dp); 92 | return; 93 | } 94 | delete request; 95 | delete view_as(dp); 96 | } 97 | 98 | public int WebHookData(const char[] data, any dp) { 99 | PrintToServer("DATA RECE: %s", data); 100 | static char stringJson[16384]; 101 | stringJson[0] = '\0'; 102 | json_dump(view_as(dp), stringJson, sizeof(stringJson), 0, true); 103 | PrintToServer("DATA SENT: %s", stringJson); 104 | } -------------------------------------------------------------------------------- /scripting/include/discord/webhook.inc: -------------------------------------------------------------------------------- 1 | methodmap DiscordWebHook < Handle { 2 | public DiscordWebHook(char[] url) { 3 | Handle mp = json_object(); 4 | json_object_set_new(mp, "__url", json_string(url)); 5 | Handle data = json_object(); 6 | json_object_set_new(mp, "__data", data); 7 | 8 | return view_as(mp); 9 | } 10 | 11 | public void GetUrl(char[] buffer, int maxlength) { 12 | JsonObjectGetString(this, "__url", buffer, maxlength); 13 | } 14 | 15 | /** 16 | * Gets/Sets if the hook should be sent as Slack. 17 | * Note: color is different for slack than discord msg. 18 | * 19 | * @return True if Slack, otherwise false. 20 | */ 21 | property bool SlackMode { 22 | public get() { 23 | return JsonObjectGetBool(this, "__slack", false); 24 | } 25 | 26 | public set(bool value) { 27 | json_object_set_new(this, "__slack", (value) ? json_true() : json_false()); 28 | } 29 | } 30 | 31 | property Handle Data { 32 | public get() { 33 | return json_object_get(this, "__data"); 34 | } 35 | 36 | public set(Handle value) { 37 | json_object_set_new(this, "__data", value); 38 | } 39 | } 40 | 41 | public void UpdateDataObject(char[] key, Handle hObject) { 42 | Handle data = this.Data; 43 | json_object_set_new(data, key, hObject); 44 | delete data; 45 | } 46 | 47 | public bool GetDataBool(char[] key, bool defaultValue=false) { 48 | Handle data = this.Data; 49 | bool value = JsonObjectGetBool(data, key, defaultValue); 50 | delete data; 51 | return value; 52 | } 53 | 54 | public bool GetDataString(char[] key, char[] buffer, int maxlength) { 55 | Handle data = this.Data; 56 | bool success = JsonObjectGetString(data, key, buffer, maxlength); 57 | delete data; 58 | return success; 59 | } 60 | 61 | /** 62 | * Note: Deletes the MessageEmbed Object! 63 | */ 64 | public void Embed(MessageEmbed Object) { 65 | //this.UpdateDataObject("embeds", Object); 66 | Handle data = this.Data; 67 | Handle hArray = json_object_get(data, "embeds"); 68 | 69 | if(hArray == null) { 70 | hArray = json_array(); 71 | json_object_set(data, "embeds", hArray); 72 | } 73 | 74 | json_array_append_new(hArray, Object); 75 | delete hArray; 76 | delete data; 77 | 78 | } 79 | 80 | property bool tts { 81 | public get() { 82 | return this.GetDataBool("tts", false); 83 | } 84 | 85 | public set(bool value) { 86 | this.UpdateDataObject("tts", json_boolean(value)); 87 | } 88 | } 89 | 90 | public bool GetUsername(char[] buffer, int maxlength) { 91 | return this.GetDataString("username", buffer, maxlength); 92 | } 93 | 94 | public void SetUsername(const char[] name) { 95 | this.UpdateDataObject("username", json_string(name)); 96 | } 97 | 98 | public bool GetAvatar(char[] buffer, int maxlength) { 99 | return this.GetDataString("icon_url", buffer, maxlength); 100 | } 101 | 102 | public void SetAvatar(const char[] icon_url) { 103 | this.UpdateDataObject("icon_url", json_string(icon_url)); 104 | } 105 | 106 | public bool GetContent(char[] buffer, int maxlength) { 107 | return this.GetDataString("content", buffer, maxlength); 108 | } 109 | 110 | public void SetContent(const char[] content) { 111 | this.UpdateDataObject("content", json_string(content)); 112 | } 113 | 114 | /*property Handle OnComplete { 115 | public get() { 116 | Handle fForward = null; 117 | if(!GetTrieValue(this, "callback", fForward)) { 118 | return null; 119 | } 120 | 121 | return fForward; 122 | } 123 | 124 | public set(Handle value) { 125 | SetTrieValue(this, "callback", value); 126 | SetTrieValue(this, "plugin", GetMyHandle()); 127 | } 128 | } 129 | 130 | property Handle CallbackPlugin { 131 | public get() { 132 | Handle value = null; 133 | if(!GetTrieValue(this, "plugin", value)) { 134 | return null; 135 | } 136 | 137 | return value; 138 | } 139 | }*/ 140 | 141 | public native void Send(); 142 | }; 143 | -------------------------------------------------------------------------------- /scripting/discord/deletemessage.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_DeleteMessageID(Handle plugin, int numParams) { 2 | DiscordBot bot = GetNativeCell(1); 3 | 4 | char channelid[64]; 5 | GetNativeString(2, channelid, sizeof(channelid)); 6 | 7 | char msgid[64]; 8 | GetNativeString(3, msgid, sizeof(msgid)); 9 | 10 | Function fCallback = GetNativeCell(4); 11 | any data = GetNativeCell(5); 12 | 13 | DataPack dp = CreateDataPack(); 14 | WritePackCell(dp, bot); 15 | WritePackString(dp, channelid); 16 | WritePackString(dp, msgid); 17 | WritePackCell(dp, plugin); 18 | WritePackFunction(dp, fCallback); 19 | WritePackCell(dp, data); 20 | 21 | ThisDeleteMessage(bot, channelid, msgid, dp); 22 | } 23 | 24 | public int Native_DiscordBot_DeleteMessage(Handle plugin, int numParams) { 25 | DiscordBot bot = GetNativeCell(1); 26 | 27 | char channelid[64]; 28 | DiscordChannel channel = GetNativeCell(2); 29 | channel.GetID(channelid, sizeof(channelid)); 30 | 31 | char msgid[64]; 32 | DiscordMessage msg = GetNativeCell(3); 33 | msg.GetID(msgid, sizeof(msgid)); 34 | 35 | Function fCallback = GetNativeCell(4); 36 | any data = GetNativeCell(5); 37 | 38 | DataPack dp = CreateDataPack(); 39 | WritePackCell(dp, bot); 40 | WritePackString(dp, channelid); 41 | WritePackString(dp, msgid); 42 | WritePackCell(dp, plugin); 43 | WritePackFunction(dp, fCallback); 44 | WritePackCell(dp, data); 45 | 46 | ThisDeleteMessage(bot, channelid, msgid, dp); 47 | } 48 | 49 | static void ThisDeleteMessage(DiscordBot bot, char[] channelid, char[] msgid, DataPack dp) { 50 | char url[64]; 51 | FormatEx(url, sizeof(url), "channels/%s/messages/%s", channelid, msgid); 52 | 53 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodDELETE, null, MessageDeletedResp); 54 | if(request == null) { 55 | CreateTimer(2.0, ThisDeleteMessageDelayed, dp); 56 | return; 57 | } 58 | 59 | SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(url)); 60 | 61 | DiscordSendRequest(request, url); 62 | } 63 | 64 | public Action ThisDeleteMessageDelayed(Handle timer, any data) { 65 | DataPack dp = view_as(data); 66 | ResetPack(dp); 67 | 68 | DiscordBot bot = ReadPackCell(dp); 69 | 70 | char channelid[32]; 71 | ReadPackString(dp, channelid, sizeof(channelid)); 72 | 73 | char msgid[32]; 74 | ReadPackString(dp, msgid, sizeof(msgid)); 75 | 76 | ThisDeleteMessage(bot, channelid, msgid, dp); 77 | } 78 | 79 | public int MessageDeletedResp(Handle request, bool failure, int offset, int statuscode, any dp) { 80 | if(failure || statuscode != 204) { 81 | if(statuscode == 429 || statuscode == 500) { 82 | ResetPack(dp); 83 | DiscordBot bot = ReadPackCell(dp); 84 | 85 | char channelid[32]; 86 | ReadPackString(dp, channelid, sizeof(channelid)); 87 | 88 | char msgid[32]; 89 | ReadPackString(dp, msgid, sizeof(msgid)); 90 | 91 | ThisDeleteMessage(bot, channelid, msgid, view_as(dp)); 92 | 93 | delete request; 94 | return; 95 | } 96 | LogError("[DISCORD] Couldn't delete message - Fail %i %i", failure, statuscode); 97 | delete request; 98 | delete view_as(dp); 99 | return; 100 | } 101 | 102 | ResetPack(dp); 103 | DiscordBot bot = ReadPackCell(dp); 104 | 105 | char channelid[32]; 106 | ReadPackString(dp, channelid, sizeof(channelid)); 107 | 108 | char msgid[32]; 109 | ReadPackString(dp, msgid, sizeof(msgid)); 110 | 111 | Handle plugin = view_as(ReadPackCell(dp)); 112 | Function func = ReadPackFunction(dp); 113 | any pluginData = ReadPackCell(dp); 114 | 115 | Handle fForward = INVALID_HANDLE; 116 | if(func != INVALID_FUNCTION) { 117 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell); 118 | AddToForward(fForward, plugin, func); 119 | 120 | Call_StartForward(fForward); 121 | Call_PushCell(bot); 122 | Call_PushCell(pluginData); 123 | Call_Finish(); 124 | delete fForward; 125 | } 126 | 127 | delete view_as(dp); 128 | delete request; 129 | } -------------------------------------------------------------------------------- /scripting/include/discord/message_embed.inc: -------------------------------------------------------------------------------- 1 | methodmap MessageEmbed < Handle { 2 | public MessageEmbed() { 3 | Handle hObj = json_object(); 4 | return view_as(hObj); 5 | } 6 | 7 | public bool GetColor(char[] buffer, int maxlength) { 8 | return JsonObjectGetString(this, "color", buffer, maxlength); 9 | } 10 | 11 | public void SetColor(const char[] color) { 12 | json_object_set_new(this, "color", json_string(color)); 13 | } 14 | 15 | public bool GetTitle(char[] buffer, int maxlength) { 16 | return JsonObjectGetString(this, "title", buffer, maxlength); 17 | } 18 | 19 | public void SetTitle(const char[] title) { 20 | json_object_set_new(this, "title", json_string(title)); 21 | } 22 | 23 | public bool GetTitleLink(char[] buffer, int maxlength) { 24 | return JsonObjectGetString(this, "title_link", buffer, maxlength); 25 | } 26 | 27 | public void SetTitleLink(const char[] title_link) { 28 | json_object_set_new(this, "title_link", json_string(title_link)); 29 | } 30 | 31 | public bool GetImage(char[] buffer, int maxlength) { 32 | return JsonObjectGetString(this, "image_url", buffer, maxlength); 33 | } 34 | 35 | public void SetImage(const char[] image_url) { 36 | json_object_set_new(this, "image_url", json_string(image_url)); 37 | } 38 | 39 | public bool GetAuthor(char[] buffer, int maxlength) { 40 | return JsonObjectGetString(this, "author_name", buffer, maxlength); 41 | } 42 | 43 | public void SetAuthor(const char[] author_name) { 44 | json_object_set_new(this, "author_name", json_string(author_name)); 45 | } 46 | 47 | public bool GetAuthorLink(char[] buffer, int maxlength) { 48 | return JsonObjectGetString(this, "author_link", buffer, maxlength); 49 | } 50 | 51 | public void SetAuthorLink(const char[] author_link) { 52 | json_object_set_new(this, "author_link", json_string(author_link)); 53 | } 54 | 55 | public bool GetAuthorIcon(char[] buffer, int maxlength) { 56 | return JsonObjectGetString(this, "author_icon", buffer, maxlength); 57 | } 58 | 59 | public void SetAuthorIcon(const char[] author_icon) { 60 | json_object_set_new(this, "author_icon", json_string(author_icon)); 61 | } 62 | 63 | public bool GetThumb(char[] buffer, int maxlength) { 64 | return JsonObjectGetString(this, "thumb_url", buffer, maxlength); 65 | } 66 | 67 | public void SetThumb(const char[] thumb_url) { 68 | json_object_set_new(this, "thumb_url", json_string(thumb_url)); 69 | } 70 | 71 | public bool GetFooter(char[] buffer, int maxlength) { 72 | return JsonObjectGetString(this, "footer", buffer, maxlength); 73 | } 74 | 75 | public void SetFooter(const char[] footer) { 76 | json_object_set_new(this, "footer", json_string(footer)); 77 | } 78 | 79 | public bool GetFooterIcon(char[] buffer, int maxlength) { 80 | return JsonObjectGetString(this, "footer_icon", buffer, maxlength); 81 | } 82 | 83 | public void SetFooterIcon(const char[] footer_icon) { 84 | json_object_set_new(this, "footer_icon", json_string(footer_icon)); 85 | } 86 | /** 87 | * Note: Setting Fields will delete the handle! 88 | */ 89 | property Handle Fields { 90 | public get() { 91 | return json_object_get(this, "fields"); 92 | } 93 | 94 | public set(Handle value) { 95 | json_object_set_new(this, "fields", value); 96 | } 97 | } 98 | 99 | public void AddField(const char[] name, const char[] value, bool inline) { 100 | Handle hObj = json_object(); 101 | json_object_set_new(hObj, "name", json_string(name)); 102 | json_object_set_new(hObj, "value", json_string(value)); 103 | json_object_set_new(hObj, "inline", json_boolean(inline)); 104 | Handle hArray = this.Fields; 105 | if(this.Fields == null) { 106 | hArray = json_array(); 107 | } 108 | json_array_append_new(hArray, hObj); 109 | this.Fields = hArray; 110 | } 111 | 112 | //Below don't support Slack Mode 113 | public bool GetDescription(char[] buffer, int maxlength) { 114 | return JsonObjectGetString(this, "description", buffer, maxlength); 115 | } 116 | 117 | public void SetDescription(const char[] description) { 118 | json_object_set_new(this, "description", json_string(description)); 119 | } 120 | 121 | public bool GetURL(char[] buffer, int maxlength) { 122 | return JsonObjectGetString(this, "url", buffer, maxlength); 123 | } 124 | 125 | public void SetURL(const char[] url) { 126 | json_object_set_new(this, "url", json_string(url)); 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /scripting/discord/SendMessage.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_SendMessageToChannel(Handle plugin, int numParams) { 2 | DiscordBot bot = GetNativeCell(1); 3 | char channel[32]; 4 | static char message[2048]; 5 | GetNativeString(2, channel, sizeof(channel)); 6 | GetNativeString(3, message, sizeof(message)); 7 | 8 | Function fCallback = GetNativeCell(4); 9 | any data = GetNativeCell(5); 10 | Handle fForward = null; 11 | if(fCallback != INVALID_FUNCTION) { 12 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); 13 | AddToForward(fForward, plugin, fCallback); 14 | } 15 | 16 | SendMessage(bot, channel, message, fForward, data); 17 | } 18 | 19 | public int Native_DiscordBot_SendMessage(Handle plugin, int numParams) { 20 | DiscordBot bot = GetNativeCell(1); 21 | 22 | DiscordChannel Channel = GetNativeCell(2); 23 | char channelID[32]; 24 | Channel.GetID(channelID, sizeof(channelID)); 25 | 26 | static char message[2048]; 27 | GetNativeString(3, message, sizeof(message)); 28 | 29 | Function fCallback = GetNativeCell(4); 30 | any data = GetNativeCell(5); 31 | Handle fForward = null; 32 | if(fCallback != INVALID_FUNCTION) { 33 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); 34 | AddToForward(fForward, plugin, fCallback); 35 | } 36 | 37 | SendMessage(bot, channelID, message, fForward, data); 38 | } 39 | 40 | public int Native_DiscordChannel_SendMessage(Handle plugin, int numParams) { 41 | DiscordChannel channel = view_as(GetNativeCell(1)); 42 | 43 | char channelID[32]; 44 | channel.GetID(channelID, sizeof(channelID)); 45 | 46 | DiscordBot bot = GetNativeCell(2); 47 | 48 | static char message[2048]; 49 | GetNativeString(3, message, sizeof(message)); 50 | 51 | Function fCallback = GetNativeCell(4); 52 | any data = GetNativeCell(5); 53 | Handle fForward = null; 54 | if(fCallback != INVALID_FUNCTION) { 55 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); 56 | AddToForward(fForward, plugin, fCallback); 57 | } 58 | 59 | SendMessage(bot, channelID, message, fForward, data); 60 | } 61 | 62 | static void SendMessage(DiscordBot bot, char[] channel, char[] message, Handle fForward, any data) { 63 | Handle hJson = json_object(); 64 | 65 | json_object_set_new(hJson, "content", json_string(message)); 66 | 67 | char url[64]; 68 | FormatEx(url, sizeof(url), "channels/%s/messages", channel); 69 | 70 | DataPack dpSafety = new DataPack(); 71 | WritePackCell(dpSafety, bot); 72 | WritePackString(dpSafety, channel); 73 | WritePackString(dpSafety, message); 74 | WritePackCell(dpSafety, fForward); 75 | WritePackCell(dpSafety, data); 76 | 77 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodPOST, hJson, GetSendMessageData); 78 | if(request == null) { 79 | delete hJson; 80 | CreateTimer(2.0, SendMessageDelayed, dpSafety); 81 | return; 82 | } 83 | 84 | SteamWorks_SetHTTPRequestContextValue(request, dpSafety, UrlToDP(url)); 85 | 86 | DiscordSendRequest(request, url); 87 | } 88 | 89 | public Action SendMessageDelayed(Handle timer, any data) { 90 | DataPack dp = view_as(data); 91 | ResetPack(dp); 92 | 93 | DiscordBot bot = ReadPackCell(dp); 94 | 95 | char channel[32]; 96 | ReadPackString(dp, channel, sizeof(channel)); 97 | 98 | char message[2048]; 99 | ReadPackString(dp, message, sizeof(message)); 100 | 101 | Handle fForward = ReadPackCell(dp); 102 | any dataa = ReadPackCell(dp); 103 | 104 | delete dp; 105 | 106 | SendMessage(bot, channel, message, fForward, dataa); 107 | } 108 | 109 | public int GetSendMessageData(Handle request, bool failure, int offset, int statuscode, any dp) { 110 | if(failure || statuscode != 200) { 111 | if(statuscode == 429 || statuscode == 500) { 112 | ResetPack(dp); 113 | DiscordBot bot = ReadPackCell(dp); 114 | 115 | char channel[32]; 116 | ReadPackString(dp, channel, sizeof(channel)); 117 | 118 | char message[2048]; 119 | ReadPackString(dp, message, sizeof(message)); 120 | 121 | Handle fForward = ReadPackCell(dp); 122 | any data = ReadPackCell(dp); 123 | 124 | delete view_as(dp); 125 | 126 | SendMessage(bot, channel, message, fForward, data); 127 | 128 | delete request; 129 | return; 130 | } 131 | LogError("[DISCORD] Couldn't Send Message - Fail %i %i", failure, statuscode); 132 | delete request; 133 | delete view_as(dp); 134 | return; 135 | } 136 | delete request; 137 | delete view_as(dp); 138 | } -------------------------------------------------------------------------------- /scripting/include/sourcebanspp.inc: -------------------------------------------------------------------------------- 1 | // ************************************************************************* 2 | // This file is part of SourceBans++. 3 | // 4 | // Copyright (C) 2014-2019 SourceBans++ Dev Team 5 | // 6 | // SourceBans++ is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, per version 3 of the License. 9 | // 10 | // SourceBans++ is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with SourceBans++. If not, see . 17 | // 18 | // This file based off work(s) covered by the following copyright(s): 19 | // 20 | // SourceBans 1.4.11 21 | // Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect 22 | // Licensed under GNU GPL version 3, or later. 23 | // Page: - 24 | // 25 | // ************************************************************************* 26 | 27 | #if defined _sourcebanspp_included 28 | #endinput 29 | #endif 30 | #define _sourcebanspp_included 31 | 32 | public SharedPlugin __pl_sourcebanspp = 33 | { 34 | name = "sourcebans++", 35 | file = "sbpp_main.smx", 36 | #if defined REQUIRE_PLUGIN 37 | required = 1 38 | #else 39 | required = 0 40 | #endif 41 | }; 42 | 43 | #if !defined REQUIRE_PLUGIN 44 | public void __pl_sourcebanspp_SetNTVOptional() 45 | { 46 | MarkNativeAsOptional("SBBanPlayer"); 47 | MarkNativeAsOptional("SBPP_BanPlayer"); 48 | MarkNativeAsOptional("SBPP_ReportPlayer"); 49 | } 50 | #endif 51 | 52 | 53 | /********************************************************* 54 | * Ban Player from server 55 | * 56 | * @param iAdmin The client index of the admin who is banning the client 57 | * @param iTarget The client index of the player to ban 58 | * @param iTime The time to ban the player for (in minutes, 0 = permanent) 59 | * @param sReason The reason to ban the player from the server 60 | * @noreturn 61 | *********************************************************/ 62 | #pragma deprecated Use SBPP_BanPlayer() instead. 63 | native void SBBanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); 64 | 65 | /********************************************************* 66 | * Ban Player from server 67 | * 68 | * @param iAdmin The client index of the admin who is banning the client 69 | * @param iTarget The client index of the player to ban 70 | * @param iTime The time to ban the player for (in minutes, 0 = permanent) 71 | * @param sReason The reason to ban the player from the server 72 | * @noreturn 73 | *********************************************************/ 74 | native void SBPP_BanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); 75 | 76 | /********************************************************* 77 | * Reports a player 78 | * 79 | * @param iReporter The client index of the reporter 80 | * @param iTarget The client index of the player to report 81 | * @param sReason The reason to report the player 82 | * @noreturn 83 | *********************************************************/ 84 | native void SBPP_ReportPlayer(int iReporter, int iTarget, const char[] sReason); 85 | 86 | /********************************************************* 87 | * Called when the admin banning the player. 88 | * 89 | * @param iAdmin The client index of the admin who is banning the client 90 | * @param iTarget The client index of the player to ban 91 | * @param iTime The time to ban the player for (in minutes, 0 = permanent) 92 | * @param sReason The reason to ban the player from the server 93 | *********************************************************/ 94 | forward void SBPP_OnBanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); 95 | 96 | /********************************************************* 97 | * Called when a new report is inserted 98 | * 99 | * @param iReporter The client index of the reporter 100 | * @param iTarget The client index of the player to report 101 | * @param sReason The reason to report the player 102 | * @noreturn 103 | *********************************************************/ 104 | forward void SBPP_OnReportPlayer(int iReporter, int iTarget, const char[] sReason); 105 | 106 | //Yarr! -------------------------------------------------------------------------------- /scripting/discord/GetGuildChannels.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_GetGuildChannels(Handle plugin, int numParams) { 2 | DiscordBot bot = GetNativeCell(1); 3 | 4 | char guild[32]; 5 | GetNativeString(2, guild, sizeof(guild)); 6 | 7 | Function fCallback = GetNativeCell(3); 8 | Function fCallbackAll = GetNativeCell(4); 9 | any data = GetNativeCell(5); 10 | 11 | DataPack dp = CreateDataPack(); 12 | WritePackCell(dp, bot); 13 | WritePackString(dp, guild); 14 | WritePackCell(dp, plugin); 15 | WritePackFunction(dp, fCallback); 16 | WritePackFunction(dp, fCallbackAll); 17 | WritePackCell(dp, data); 18 | 19 | ThisSendRequest(bot, guild, dp); 20 | } 21 | 22 | static void ThisSendRequest(DiscordBot bot, char[] guild, DataPack dp) { 23 | char url[64]; 24 | FormatEx(url, sizeof(url), "guilds/%s/channels", guild); 25 | 26 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodGET, null, GetGuildChannelsData); 27 | if(request == null) { 28 | CreateTimer(2.0, GetGuildChannelsDelayed, dp); 29 | return; 30 | } 31 | 32 | SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(url)); 33 | 34 | DiscordSendRequest(request, url); 35 | } 36 | 37 | public Action GetGuildChannelsDelayed(Handle timer, any data) { 38 | DataPack dp = view_as(data); 39 | ResetPack(dp); 40 | 41 | DiscordBot bot = ReadPackCell(dp); 42 | 43 | char guild[32]; 44 | ReadPackString(dp, guild, sizeof(guild)); 45 | 46 | ThisSendRequest(bot, guild, dp); 47 | } 48 | 49 | public int GetGuildChannelsData(Handle request, bool failure, int offset, int statuscode, any dp) { 50 | if(failure || statuscode != 200) { 51 | if(statuscode == 429 || statuscode == 500) { 52 | ResetPack(dp); 53 | DiscordBot bot = ReadPackCell(dp); 54 | 55 | char guild[32]; 56 | ReadPackString(dp, guild, sizeof(guild)); 57 | 58 | ThisSendRequest(bot, guild, view_as(dp)); 59 | 60 | delete request; 61 | return; 62 | } 63 | LogError("[DISCORD] Couldn't Retrieve Guild Channels - Fail %i %i", failure, statuscode); 64 | delete request; 65 | delete view_as(dp); 66 | return; 67 | } 68 | SteamWorks_GetHTTPResponseBodyCallback(request, GetGuildChannelsData_Data, dp); 69 | delete request; 70 | } 71 | 72 | public int GetGuildChannelsData_Data(const char[] data, any datapack) { 73 | Handle hJson = json_load(data); 74 | 75 | //Read from datapack to get info 76 | Handle dp = view_as(datapack); 77 | ResetPack(dp); 78 | int bot = ReadPackCell(dp); 79 | 80 | char guild[32]; 81 | ReadPackString(dp, guild, sizeof(guild)); 82 | 83 | Handle plugin = view_as(ReadPackCell(dp)); 84 | Function func = ReadPackFunction(dp); 85 | Function funcAll = ReadPackFunction(dp); 86 | any pluginData = ReadPackCell(dp); 87 | delete dp; 88 | 89 | //Create forwards 90 | Handle fForward = INVALID_HANDLE; 91 | Handle fForwardAll = INVALID_HANDLE; 92 | if(func != INVALID_FUNCTION) { 93 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell); 94 | AddToForward(fForward, plugin, func); 95 | } 96 | 97 | if(funcAll != INVALID_FUNCTION) { 98 | fForwardAll = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell); 99 | AddToForward(fForwardAll, plugin, funcAll); 100 | } 101 | 102 | ArrayList alChannels = null; 103 | 104 | if(funcAll != INVALID_FUNCTION) { 105 | alChannels = CreateArray(); 106 | } 107 | 108 | //Loop through json 109 | for(int i = 0; i < json_array_size(hJson); i++) { 110 | Handle hObject = json_array_get(hJson, i); 111 | 112 | DiscordChannel Channel = view_as(hObject); 113 | 114 | if(fForward != INVALID_HANDLE) { 115 | Call_StartForward(fForward); 116 | Call_PushCell(bot); 117 | Call_PushString(guild); 118 | Call_PushCell(Channel); 119 | Call_PushCell(pluginData); 120 | Call_Finish(); 121 | } 122 | 123 | if(fForwardAll != INVALID_HANDLE) { 124 | alChannels.Push(Channel); 125 | }else { 126 | delete Channel; 127 | } 128 | } 129 | 130 | if(fForwardAll != INVALID_HANDLE) { 131 | Call_StartForward(fForwardAll); 132 | Call_PushCell(bot); 133 | Call_PushString(guild); 134 | Call_PushCell(alChannels); 135 | Call_PushCell(pluginData); 136 | Call_Finish(); 137 | 138 | for(int i = 0; i < alChannels.Length; i++) { 139 | Handle hChannel = view_as(alChannels.Get(i)); 140 | delete hChannel; 141 | } 142 | 143 | delete alChannels; 144 | delete fForwardAll; 145 | } 146 | 147 | if(fForward != INVALID_HANDLE) { 148 | delete fForward; 149 | } 150 | 151 | delete hJson; 152 | } -------------------------------------------------------------------------------- /translations/dsm.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Manager.YourID" 4 | { 5 | "#format" "{1:s}" 6 | "en" "Your ID: {grey}{1}" 7 | "hu" "Azonosítód: {grey}{1}" 8 | "fr" "Identifiant: {grey}{1}" 9 | } 10 | 11 | "Manager.Connect" 12 | { 13 | "#format" "{1:s}" 14 | "en" "Connect to our discord server at: {blue}{1}" 15 | "hu" "Csatlakozz discord szerverünkre: {blue}{1}" 16 | "fr" "Connectez-vous à notre serveur de discord avec: {blue}{1}" 17 | } 18 | 19 | "Manager.Usage" 20 | { 21 | "#format" "{1:s},{2:s},{3:s}" 22 | "en" "Use {yellow}{1} {2}{default} in channel {purple}#{3} {default}to get verified!" 23 | "hu" "Használd a {yellow}{1} {2}{default} parancsot a {purple}#{3} {default}szobában a megerősítéshez!" 24 | "fr" "Utiliser {yellow}{1} {2}{default} dans le canal {purple}#{3} {default}pour se faire vérifier!" 25 | } 26 | 27 | "Manager.MustVerify" 28 | { 29 | "#format" "{1:s}" 30 | "en" "You have to verify your {blue}Discord {default}account! Type {gold}{1}" 31 | "hu" "Meg kell erősítened a {blue}Discord {default}fiókodat! Írd be, hogy {gold}{1}" 32 | "fr" "Vous devez vérifier votre {blue}Discord {default}account! Ecrit {gold}{1}" 33 | } 34 | 35 | "Manager.AlreadyLinked" 36 | { 37 | "#format" "{1:s}" 38 | "en" "<@{1}> This discord account is already linked! ❌" 39 | "hu" "<@{1}> Ez a discord fiók már társítva van! ❌" 40 | "fr" "<@{1}> Ce compte Discord est déjà lié! ❌" 41 | } 42 | 43 | "Manager.Linked" 44 | { 45 | "#format" "{1:s}" 46 | "en" "<@{1}> You've linked your account! ✅" 47 | "hu" "<@{1}> Sikeresen megerősítetted a fiókodat! ✅ \nmostmár visszamehetsz a szerverre^-^" 48 | "fr" "<@{1}> Vous avez lié votre compte! ✅" 49 | } 50 | 51 | "Manager.Verified" 52 | { 53 | "#format" "{1:s},{2:s}" 54 | "en" "You have verified your account. ({green}{1}#{2}{default})" 55 | "hu" "Megerősítetted a fiókodat! ({green}{1}#{2}{default})" 56 | "fr" "Vous avez validé votre compte. ({green}{1}#{2}{default})" 57 | } 58 | 59 | "Manager.Info" 60 | { 61 | "#format" "{1:s},{2:s}" 62 | "en" "<@{1}> This channel is being used for account linking!\nUsage: {2} " 63 | "hu" "<@{1}> Ezt a szobát a fiókok megerősítésére használjuk!\nHasználat: {2} " 64 | "fr" "<@{1}> Ce canal est utilisé pour la liason de compte!\nUtilisation: {2} " 65 | } 66 | 67 | "Manager.Invalid" 68 | { 69 | "#format" "{1:s}" 70 | "en" "<@{1}> This code is invalid!" 71 | "hu" "<@{1}> Ez a megerősítő kód nem érvényes!" 72 | "fr" "<@{1}> Ce code n'est pas valable!" 73 | } 74 | 75 | "Modules.WebHook.LengthTime" 76 | { 77 | "#format" "{1:d}" 78 | "en" "{1} minute" 79 | "hu" "{1} perc" 80 | "fr" "{1} minute" 81 | } 82 | 83 | "Modules.WebHook.LengthTemp" 84 | { 85 | "en" "TEMPORARY" 86 | "hu" "IDEIGLENES" 87 | "fr" "TEMPORAIRE" 88 | } 89 | 90 | "Modules.WebHook.LengthPerm" 91 | { 92 | "en" "PERMANENT" 93 | "hu" "VÉGLEGES" 94 | "fr" "PERMANENT" 95 | } 96 | 97 | "Modules.WebHook.AdminField" 98 | { 99 | "en" "ADMIN" 100 | "hu" "ADMIN" 101 | "fr" "ADMIN" 102 | } 103 | 104 | "Modules.WebHook.TargetField" 105 | { 106 | "en" "TARGET" 107 | "hu" "JÁTÉKOS" 108 | "fr" "CIBLE" 109 | } 110 | 111 | "Modules.WebHook.LengthField" 112 | { 113 | "en" "LENGTH" 114 | "hu" "IDŐTARTAM" 115 | "fr" "DURÉE" 116 | } 117 | 118 | "Modules.WebHook.TypeField" 119 | { 120 | "en" "TYPE" 121 | "hu" "TIPUS" 122 | "fr" "TYPE" 123 | } 124 | 125 | "Modules.WebHook.ReasonField" 126 | { 127 | "en" "REASON" 128 | "hu" "INDOK" 129 | "fr" "RAISON" 130 | } 131 | 132 | "Modules.WebHook.CurrentMapField" 133 | { 134 | "en" "Current Map:" 135 | "hu" "Jelenlegi pálya:" 136 | "fr" "Carte actuelle:" 137 | } 138 | 139 | "Modules.WebHook.PlayersOnlineField" 140 | { 141 | "en" "Players Online:" 142 | "hu" "Elérhető Játékosok:" 143 | "fr" "Joueurs en ligne:" 144 | } 145 | 146 | "Modules.WebHook.DirectConnectField" 147 | { 148 | "en" "Direct Connect:" 149 | "hu" "Csatlakozás:" 150 | "fr" "Connexion directe:" 151 | } 152 | 153 | "Modules.ChatRelay.InvalidName" 154 | { 155 | "en" "INVALID NAME" 156 | "hu" "NÉVTELEN" 157 | "fr" "NOM INCORRECT" 158 | } 159 | 160 | "Modules.WebHook.NoReason" 161 | { 162 | "en" "No reason specified" 163 | "hu" "Nincs indok megadva" 164 | } 165 | 166 | "Modules.WebHook.Player" 167 | { 168 | "en" "PLAYER" 169 | "hu" "JÁTÉKOS" 170 | } 171 | } -------------------------------------------------------------------------------- /scripting/discord/ListenToChannel.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_StartTimer(Handle plugin, int numParams) { 2 | DiscordBot bot = GetNativeCell(1); 3 | DiscordChannel channel = GetNativeCell(2); 4 | Function func = GetNativeCell(3); 5 | 6 | Handle hObj = json_object(); 7 | json_object_set(hObj, "bot", bot); 8 | json_object_set(hObj, "channel", channel); 9 | 10 | Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell); 11 | AddToForward(fwd, plugin, func); 12 | 13 | json_object_set_new(hObj, "callback", json_integer(view_as(fwd))); 14 | 15 | GetMessages(hObj); 16 | } 17 | 18 | public void GetMessages(Handle hObject) { 19 | DiscordBot bot = view_as(json_object_get(hObject, "bot")); 20 | DiscordChannel channel = view_as(json_object_get(hObject, "channel")); 21 | //Handle fwd = view_as(json_object_get(hObject, "callback")); 22 | 23 | char channelID[32]; 24 | channel.GetID(channelID, sizeof(channelID)); 25 | 26 | char lastMessage[64]; 27 | channel.GetLastMessageID(lastMessage, sizeof(lastMessage)); 28 | 29 | char url[256]; 30 | FormatEx(url, sizeof(url), "channels/%s/messages?limit=%i&after=%s", channelID, 100, lastMessage); 31 | 32 | Handle request = PrepareRequest(bot, url, _, null, OnGetMessage); 33 | if(request == null) { 34 | delete bot; 35 | delete channel; 36 | CreateTimer(2.0, GetMessagesDelayed, hObject); 37 | return; 38 | } 39 | 40 | char route[128]; 41 | FormatEx(route, sizeof(route), "channels/%s", channelID); 42 | 43 | SteamWorks_SetHTTPRequestContextValue(request, hObject, UrlToDP(route)); 44 | 45 | delete bot; 46 | delete channel; 47 | 48 | DiscordSendRequest(request, route); 49 | } 50 | 51 | public Action GetMessagesDelayed(Handle timer, any data) { 52 | GetMessages(view_as(data)); 53 | } 54 | 55 | public Action CheckMessageTimer(Handle timer, any dpt) { 56 | GetMessages(view_as(dpt)); 57 | } 58 | 59 | public int OnGetMessage(Handle request, bool failure, int offset, int statuscode, any dp) { 60 | if(failure || statuscode != 200) { 61 | if(statuscode == 429 || statuscode == 500) { 62 | GetMessages(view_as(dp)); 63 | delete request; 64 | return; 65 | } 66 | LogError("[DISCORD] Couldn't Retrieve Messages - Fail %i %i", failure, statuscode); 67 | delete request; 68 | Handle fwd = view_as(JsonObjectGetInt(view_as(dp), "callback")); 69 | if(fwd != null) delete fwd; 70 | delete view_as(dp); 71 | return; 72 | } 73 | 74 | SteamWorks_GetHTTPResponseBodyCallback(request, OnGetMessage_Data, dp); 75 | delete request; 76 | } 77 | 78 | public int OnGetMessage_Data(const char[] data, any dpt) { 79 | Handle hObj = view_as(dpt); 80 | 81 | DiscordBot Bot = view_as(json_object_get(hObj, "bot")); 82 | DiscordChannel channel = view_as(json_object_get(hObj, "channel")); 83 | Handle fwd = view_as(JsonObjectGetInt(hObj, "callback")); 84 | 85 | if(!Bot.IsListeningToChannel(channel) || GetForwardFunctionCount(fwd) == 0) { 86 | delete Bot; 87 | delete channel; 88 | delete hObj; 89 | delete fwd; 90 | return; 91 | } 92 | 93 | Handle hJson = json_load(data); 94 | 95 | if(json_is_array(hJson)) { 96 | for(int i = json_array_size(hJson) - 1; i >= 0; i--) { 97 | Handle hObject = json_array_get(hJson, i); 98 | 99 | //The reason we find Channel for each message instead of global incase 100 | //Bot stops listening for the channel while we are still sending messages 101 | char channelID[32]; 102 | JsonObjectGetString(hObject, "channel_id", channelID, sizeof(channelID)); 103 | 104 | //Find Channel corresponding to Channel id 105 | //DiscordChannel Channel = Bot.GetListeningChannelByID(channelID); 106 | if(!Bot.IsListeningToChannelID(channelID)) { 107 | //Channel is no longer listed to, remove any handles & stop 108 | delete hObject; 109 | delete hJson; 110 | 111 | delete fwd; 112 | delete Bot; 113 | delete channel; 114 | delete hObj; 115 | return; 116 | } 117 | 118 | char id[32]; 119 | JsonObjectGetString(hObject, "id", id, sizeof(id)); 120 | 121 | if(i == 0) { 122 | channel.SetLastMessageID(id); 123 | } 124 | 125 | //Get info and fire forward 126 | if(fwd != null) { 127 | Call_StartForward(fwd); 128 | Call_PushCell(Bot); 129 | Call_PushCell(channel); 130 | Call_PushCell(view_as(hObject)); 131 | Call_Finish(); 132 | } 133 | 134 | delete hObject; 135 | } 136 | } 137 | 138 | CreateTimer(Bot.MessageCheckInterval, CheckMessageTimer, hObj); 139 | 140 | delete Bot; 141 | delete channel; 142 | 143 | 144 | delete hJson; 145 | } -------------------------------------------------------------------------------- /scripting/include/discord/message.inc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | methodmap DiscordMessage < JSON_Object { 4 | public bool GetID(char[] buffer, int maxlength) { 5 | return JsonObjectGetString(this, "id", buffer, maxlength); 6 | } 7 | 8 | public bool IsPinned() { 9 | return JsonObjectGetBool(this, "pinned"); 10 | } 11 | 12 | public DiscordUser GetAuthor() { 13 | return view_as(this.GetObject("author")); 14 | } 15 | 16 | public bool GetContent(char[] buffer, int maxlength) { 17 | return JsonObjectGetString(this, "content", buffer, maxlength); 18 | } 19 | 20 | public bool GetChannelID(char[] buffer, int maxlength) { 21 | return JsonObjectGetString(this, "channel_id", buffer, maxlength); 22 | } 23 | } 24 | 25 | methodmap DiscordEmbed < JSON_Object { 26 | public DiscordEmbed() { 27 | JSON_Object hObj = new JSON_Object(); 28 | return view_as(hObj); 29 | } 30 | 31 | public bool GetColor(char[] buffer, int maxlength) { 32 | return JsonObjectGetString(this, "color", buffer, maxlength); 33 | } 34 | 35 | public void SetColor(const char[] color) { 36 | this.SetString("color", color); 37 | } 38 | 39 | public bool GetTitle(char[] buffer, int maxlength) { 40 | return JsonObjectGetString(this, "title", buffer, maxlength); 41 | } 42 | 43 | public void SetTitle(const char[] title) { 44 | this.SetString("title", title); 45 | } 46 | 47 | public bool GetTitleLink(char[] buffer, int maxlength) { 48 | return JsonObjectGetString(this, "title_link", buffer, maxlength); 49 | } 50 | 51 | public void SetTitleLink(const char[] title_link) { 52 | this.SetString("title_link", title_link); 53 | } 54 | 55 | public bool GetImage(char[] buffer, int maxlength) { 56 | return JsonObjectGetString(this, "image_url", buffer, maxlength); 57 | } 58 | 59 | public void SetImage(const char[] image_url) { 60 | this.SetString("image_url", image_url); 61 | } 62 | 63 | public bool GetAuthor(char[] buffer, int maxlength) { 64 | return JsonObjectGetString(this, "author_name", buffer, maxlength); 65 | } 66 | 67 | public void SetAuthor(const char[] author_name) { 68 | this.SetString("author_name", author_name); 69 | } 70 | 71 | public bool GetAuthorLink(char[] buffer, int maxlength) { 72 | return JsonObjectGetString(this, "author_link", buffer, maxlength); 73 | } 74 | 75 | public void SetAuthorLink(const char[] author_link) { 76 | this.SetString("author_link", author_link); 77 | } 78 | 79 | public bool GetAuthorIcon(char[] buffer, int maxlength) { 80 | return JsonObjectGetString(this, "author_icon", buffer, maxlength); 81 | } 82 | 83 | public void SetAuthorIcon(const char[] author_icon) { 84 | this.SetString("author_icon", author_icon); 85 | } 86 | 87 | public bool GetThumb(char[] buffer, int maxlength) { 88 | return JsonObjectGetString(this, "thumb_url", buffer, maxlength); 89 | } 90 | 91 | public void SetThumb(const char[] thumb_url) { 92 | this.SetString("thumb_url", thumb_url); 93 | } 94 | 95 | public bool GetFooter(char[] buffer, int maxlength) { 96 | return JsonObjectGetString(this, "footer", buffer, maxlength); 97 | } 98 | 99 | public void SetFooter(const char[] footer) { 100 | this.SetString("footer", footer); 101 | } 102 | 103 | public bool GetFooterIcon(char[] buffer, int maxlength) { 104 | return JsonObjectGetString(this, "footer_icon", buffer, maxlength); 105 | } 106 | 107 | public void SetFooterIcon(const char[] footer_icon) { 108 | this.SetString("footer_icon", footer_icon); 109 | } 110 | 111 | property JSON_Array Fields { 112 | public get() { 113 | return view_as(this.GetObject("fields")); 114 | } 115 | 116 | public set(JSON_Array value) { 117 | if(this.HasKey("fields")) 118 | { 119 | this.GetObject("fields").Cleanup(); 120 | delete this.GetObject("fields"); 121 | } 122 | this.SetObject("fields", value); 123 | } 124 | } 125 | 126 | public void AddField(const char[] name, const char[] value, bool inline) { 127 | JSON_Object hObj = new JSON_Object(); 128 | hObj.SetString("name", name); 129 | hObj.SetString("value", value); 130 | hObj.SetBool("inline", inline); 131 | 132 | JSON_Array hArray = this.Fields; 133 | if(hArray == null) { 134 | hArray = new JSON_Array(); 135 | this.Fields = hArray; 136 | } 137 | hArray.PushObject(hObj); 138 | } 139 | 140 | public bool GetDescription(char[] buffer, int maxlength) { 141 | return JsonObjectGetString(this, "description", buffer, maxlength); 142 | } 143 | 144 | public void SetDescription(const char[] description) { 145 | this.SetString("description", description); 146 | } 147 | 148 | public bool GetURL(char[] buffer, int maxlength) { 149 | return JsonObjectGetString(this, "url", buffer, maxlength); 150 | } 151 | 152 | public void SetURL(const char[] url) { 153 | this.SetString("url", url); 154 | } 155 | }; -------------------------------------------------------------------------------- /scripting/include/discord.inc: -------------------------------------------------------------------------------- 1 | #if defined _discord_included 2 | #endinput 3 | #endif 4 | #define _discord_included 5 | 6 | #include 7 | #include 8 | 9 | typedef DiscordGuildsRetrieve = function void (DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data); 10 | 11 | typedef DiscordGuildsRetrievedAll = function void (DiscordBot bot, ArrayList id, ArrayList name, ArrayList icon, ArrayList owner, ArrayList permissions, any data); 12 | 13 | //Channel are Handles that are closed immediately after forwards called. To keep, clone. Or store id if thats what you want 14 | typedef DiscordGuildChannelsRetrieve = function void (DiscordBot bot, char[] guild, DiscordChannel Channel, any data); 15 | 16 | typedef DiscordGuildChannelsRetrieveAll = function void (DiscordBot bot, char[] guild, ArrayList Channels, any data); 17 | 18 | typedef DiscordGuildGetRoles = function void (DiscordBot bot, char[] guild, RoleList Roles, any data); 19 | 20 | /** 21 | * Called when message is received 22 | * bot/channel/message are all destroyed after callback is sent. 23 | * You can clone it if need to keep. 24 | */ 25 | typeset OnChannelMessage { 26 | function void(DiscordBot bot, DiscordChannel channel, DiscordMessage message); 27 | }; 28 | 29 | typedef OnGetReactions = function void (DiscordBot bot, ArrayList Users, char[] channelID, const char[] messageID, const char[] emoji, any data); 30 | 31 | typedef OnMessageSent = function void(DiscordBot bot, char[] channel, DiscordMessage message, any data); 32 | 33 | typedef OnMessageDeleted = function void(DiscordBot bot, any data); 34 | 35 | //hMemberList is JSON array containing guild members 36 | typedef OnGetMembers = function void(DiscordBot bot, char[] guild, Handle hMemberList); 37 | 38 | methodmap Role < Handle { 39 | public void GetID(char[] buffer, int maxlength) { 40 | JsonObjectGetString(this, "id", buffer, maxlength); 41 | } 42 | 43 | public void GetName(char[] buffer, int maxlength) { 44 | JsonObjectGetString(this, "name", buffer, maxlength); 45 | } 46 | 47 | public int GetColor() { 48 | return JsonObjectGetInt(this, "color"); 49 | } 50 | 51 | public int GetPosition() { 52 | return JsonObjectGetInt(this, "position"); 53 | } 54 | 55 | public int GetPermissions() { 56 | return JsonObjectGetInt(this, "permissions"); 57 | } 58 | 59 | public bool Hoist() { 60 | return JsonObjectGetBool(this, "hoist"); 61 | } 62 | 63 | public bool Managed() { 64 | return JsonObjectGetBool(this, "managed"); 65 | } 66 | 67 | public bool Mentionable() { 68 | return JsonObjectGetBool(this, "mentionable"); 69 | } 70 | }; 71 | 72 | methodmap RoleList < Handle { 73 | property int Size { 74 | public get() { 75 | return json_array_size(this); 76 | } 77 | } 78 | public Role GetRole(int i) { 79 | return view_as( 80 | json_array_get(this, i) 81 | ); 82 | } 83 | public Role Get(int i) { 84 | return this.GetRole(i); 85 | } 86 | 87 | }; 88 | 89 | /* 90 | { 91 | "id": "80351110224678912", 92 | "username": "Nelly", 93 | "discriminator": "1337", 94 | "avatar": "8342729096ea3675442027381ff50dfe", 95 | "verified": true, 96 | "email": "nelly@discordapp.com" 97 | } 98 | */ 99 | //It's a JSON Handle with the above info TODO stop using natives! 100 | methodmap DiscordUser < Handle { 101 | public native void GetID(char[] buffer, int maxlength); 102 | 103 | public native void GetUsername(char[] buffer, int maxlength); 104 | 105 | public native void GetDiscriminator(char[] buffer, int maxlength); 106 | public int GetDiscriminatorInt() { 107 | char buffer[16]; 108 | this.GetDiscriminator(buffer, sizeof(buffer)); 109 | return StringToInt(buffer); 110 | } 111 | 112 | public native void GetAvatar(char[] buffer, int maxlength); 113 | 114 | public native bool IsVerified(); 115 | 116 | public native void GetEmail(char[] buffer, int maxlength); 117 | 118 | public native bool IsBot(); 119 | }; 120 | 121 | /* 122 | 123 | {"timestamp": "2017-01-15T20:26:35.353000+00:00", "mention_everyone": false, "id": "270287641155469313", "pinned": false, "edited_timestamp": null, "author": {"username": "DK-Bot", "discriminator": "6274", "bot": true, "id": "186256454863290369", "avatar": null}, "mention_roles": [], "content": "ab", "channel_id": "229677130483499008", "mentions": [], "type": 0} 124 | */ 125 | methodmap DiscordMessage < Handle { 126 | public native void GetID(char[] buffer, int maxlength); 127 | 128 | public native bool IsPinned(); 129 | 130 | public native DiscordUser GetAuthor(); 131 | 132 | public native void GetContent(char[] buffer, int maxlength); 133 | 134 | public native void GetChannelID(char[] buffer, int maxlength); 135 | }; 136 | 137 | #include 138 | #include 139 | #include 140 | #include 141 | #include 142 | -------------------------------------------------------------------------------- /scripting/discord/GetGuilds.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_GetGuilds(Handle plugin, int numParams) { 2 | DiscordBot bot = GetNativeCell(1); 3 | Function fCallback = GetNativeCell(2); 4 | Function fCallbackAll = GetNativeCell(3); 5 | any data = GetNativeCell(4); 6 | 7 | DataPack dp = CreateDataPack(); 8 | WritePackCell(dp, bot); 9 | WritePackCell(dp, plugin); 10 | WritePackFunction(dp, fCallback); 11 | WritePackFunction(dp, fCallbackAll); 12 | WritePackCell(dp, data); 13 | 14 | ThisSendRequest(bot, dp); 15 | } 16 | 17 | static void ThisSendRequest(DiscordBot bot, DataPack dp) { 18 | char url[64]; 19 | FormatEx(url, sizeof(url), "users/@me/guilds"); 20 | 21 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodGET, null, GetGuildsData); 22 | if(request == null) { 23 | CreateTimer(2.0, GetGuildsDelayed, dp); 24 | return; 25 | } 26 | 27 | SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(url)); 28 | 29 | DiscordSendRequest(request, url); 30 | } 31 | 32 | public Action GetGuildsDelayed(Handle timer, any data) { 33 | DataPack dp = view_as(data); 34 | ResetPack(dp); 35 | 36 | DiscordBot bot = ReadPackCell(dp); 37 | 38 | ThisSendRequest(bot, dp); 39 | } 40 | 41 | public int GetGuildsData(Handle request, bool failure, int offset, int statuscode, any dp) { 42 | if(failure || statuscode != 200) { 43 | if(statuscode == 429 || statuscode == 500) { 44 | ResetPack(dp); 45 | DiscordBot bot = ReadPackCell(dp); 46 | ThisSendRequest(bot, dp); 47 | 48 | delete request; 49 | return; 50 | } 51 | LogError("[DISCORD] Couldn't Retrieve Guilds - Fail %i %i", failure, statuscode); 52 | delete request; 53 | delete view_as(dp); 54 | return; 55 | } 56 | SteamWorks_GetHTTPResponseBodyCallback(request, GetGuildsData_Data, dp); 57 | delete request; 58 | } 59 | 60 | public int GetGuildsData_Data(const char[] data, any datapack) { 61 | Handle hJson = json_load(data); 62 | 63 | //Read from datapack to get info 64 | Handle dp = view_as(datapack); 65 | ResetPack(dp); 66 | int bot = ReadPackCell(dp); 67 | Handle plugin = view_as(ReadPackCell(dp)); 68 | Function func = ReadPackFunction(dp); 69 | Function funcAll = ReadPackFunction(dp); 70 | any pluginData = ReadPackCell(dp); 71 | delete dp; 72 | 73 | //Create forwards 74 | Handle fForward = INVALID_HANDLE; 75 | Handle fForwardAll = INVALID_HANDLE; 76 | if(func != INVALID_FUNCTION) { 77 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_String, Param_String, Param_Cell, Param_Cell, Param_Cell); 78 | AddToForward(fForward, plugin, func); 79 | } 80 | 81 | if(funcAll != INVALID_FUNCTION) { 82 | fForwardAll = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell); 83 | AddToForward(fForwardAll, plugin, funcAll); 84 | } 85 | 86 | ArrayList alId = null; 87 | ArrayList alName = null; 88 | ArrayList alIcon = null; 89 | ArrayList alOwner = null; 90 | ArrayList alPermissions = null; 91 | 92 | if(funcAll != INVALID_FUNCTION) { 93 | alId = CreateArray(32); 94 | alName = CreateArray(64); 95 | alIcon = CreateArray(128); 96 | alOwner = CreateArray(); 97 | alPermissions = CreateArray(); 98 | } 99 | 100 | //Loop through json 101 | for(int i = 0; i < json_array_size(hJson); i++) { 102 | Handle hObject = json_array_get(hJson, i); 103 | 104 | static char id[32]; 105 | static char name[64]; 106 | static char icon[128]; 107 | bool owner = false; 108 | int permissions; 109 | 110 | JsonObjectGetString(hObject, "id", id, sizeof(id)); 111 | JsonObjectGetString(hObject, "name", name, sizeof(name)); 112 | JsonObjectGetString(hObject, "icon", icon, sizeof(icon)); 113 | 114 | owner = JsonObjectGetBool(hObject, "owner"); 115 | permissions = JsonObjectGetBool(hObject, "permissions"); 116 | 117 | if(fForward != INVALID_HANDLE) { 118 | Call_StartForward(fForward); 119 | Call_PushCell(bot); 120 | Call_PushString(id); 121 | Call_PushString(name); 122 | Call_PushString(icon); 123 | Call_PushCell(owner); 124 | Call_PushCell(permissions); 125 | Call_PushCell(pluginData); 126 | Call_Finish(); 127 | } 128 | 129 | if(fForwardAll != INVALID_HANDLE) { 130 | alId.PushString(id); 131 | alName.PushString(name); 132 | alIcon.PushString(icon); 133 | alOwner.Push(owner); 134 | alPermissions.Push(permissions); 135 | } 136 | 137 | delete hObject; 138 | } 139 | 140 | if(fForwardAll != INVALID_HANDLE) { 141 | Call_StartForward(fForwardAll); 142 | Call_PushCell(bot); 143 | Call_PushCell(alId); 144 | Call_PushCell(alName); 145 | Call_PushCell(alIcon); 146 | Call_PushCell(alOwner); 147 | Call_PushCell(alPermissions); 148 | Call_PushCell(pluginData); 149 | Call_Finish(); 150 | 151 | delete alId; 152 | delete alName; 153 | delete alIcon; 154 | delete alOwner; 155 | delete alPermissions; 156 | 157 | delete fForwardAll; 158 | } 159 | 160 | if(fForward != INVALID_HANDLE) { 161 | delete fForward; 162 | } 163 | 164 | delete hJson; 165 | } -------------------------------------------------------------------------------- /scripting/discord/GuildMembers.sp: -------------------------------------------------------------------------------- 1 | /** 2 | * public native void GetGuildMembers(char[] guild, OnGetMembers fCallback, char[] afterUserID="", int limit=250); 3 | */ 4 | public int Native_DiscordBot_GetGuildMembers(Handle plugin, int numParams) { 5 | DiscordBot bot = view_as(CloneHandle(GetNativeCell(1))); 6 | 7 | char guild[32]; 8 | GetNativeString(2, guild, sizeof(guild)); 9 | 10 | Function fCallback = GetNativeCell(3); 11 | 12 | int limit = GetNativeCell(4); 13 | 14 | char afterID[32]; 15 | GetNativeString(5, afterID, sizeof(afterID)); 16 | 17 | Handle hData = json_object(); 18 | json_object_set_new(hData, "bot", bot); 19 | json_object_set_new(hData, "guild", json_string(guild)); 20 | json_object_set_new(hData, "limit", json_integer(limit)); 21 | json_object_set_new(hData, "afterID", json_string(afterID)); 22 | 23 | Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell); 24 | AddToForward(fwd, plugin, fCallback); 25 | json_object_set_new(hData, "callback", json_integer(view_as(fwd))); 26 | 27 | GetMembers(hData); 28 | } 29 | 30 | public int Native_DiscordBot_GetGuildMembersAll(Handle plugin, int numParams) { 31 | DiscordBot bot = view_as(CloneHandle(GetNativeCell(1))); 32 | 33 | char guild[32]; 34 | GetNativeString(2, guild, sizeof(guild)); 35 | 36 | Function fCallback = GetNativeCell(3); 37 | 38 | int limit = GetNativeCell(4); 39 | 40 | char afterID[32]; 41 | GetNativeString(5, afterID, sizeof(afterID)); 42 | 43 | Handle hData = json_object(); 44 | json_object_set_new(hData, "bot", bot); 45 | json_object_set_new(hData, "guild", json_string(guild)); 46 | json_object_set_new(hData, "limit", json_integer(limit)); 47 | json_object_set_new(hData, "afterID", json_string(afterID)); 48 | 49 | Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell); 50 | AddToForward(fwd, plugin, fCallback); 51 | json_object_set_new(hData, "callback", json_integer(view_as(fwd))); 52 | 53 | GetMembers(hData); 54 | } 55 | 56 | static void GetMembers(Handle hData) { 57 | DiscordBot bot = view_as(json_object_get(hData, "bot")); 58 | 59 | char guild[32]; 60 | JsonObjectGetString(hData, "guild", guild, sizeof(guild)); 61 | 62 | int limit = JsonObjectGetInt(hData, "limit"); 63 | 64 | char afterID[32]; 65 | JsonObjectGetString(hData, "afterID", afterID, sizeof(afterID)); 66 | 67 | char url[256]; 68 | if(StrEqual(afterID, "")) { 69 | FormatEx(url, sizeof(url), "https://discordapp.com/api/guilds/%s/members?limit=%i", guild, limit); 70 | }else { 71 | FormatEx(url, sizeof(url), "https://discordapp.com/api/guilds/%s/members?limit=%i&afterID=%s", guild, limit, afterID); 72 | } 73 | 74 | char route[128]; 75 | FormatEx(route, sizeof(route), "guild/%s/members", guild); 76 | 77 | DiscordRequest request = new DiscordRequest(url, k_EHTTPMethodGET); 78 | if(request == null) { 79 | delete bot; 80 | CreateTimer(2.0, SendGetMembers, hData); 81 | return; 82 | } 83 | request.SetCallbacks(HTTPCompleted, MembersDataReceive); 84 | request.SetBot(bot); 85 | request.SetData(hData, route); 86 | 87 | request.Send(route); 88 | 89 | delete bot; 90 | } 91 | 92 | public Action SendGetMembers(Handle timer, any data) { 93 | GetMembers(view_as(data)); 94 | } 95 | 96 | 97 | public MembersDataReceive(Handle request, bool failure, int offset, int statuscode, any dp) { 98 | if(failure || (statuscode != 200)) { 99 | if(statuscode == 400) { 100 | PrintToServer("BAD REQUEST"); 101 | } 102 | 103 | if(statuscode == 429 || statuscode == 500) { 104 | GetMembers(dp); 105 | 106 | delete request; 107 | return; 108 | } 109 | LogError("[DISCORD] Couldn't Send GetMembers - Fail %i %i", failure, statuscode); 110 | delete request; 111 | delete view_as(dp); 112 | return; 113 | } 114 | SteamWorks_GetHTTPResponseBodyCallback(request, GetMembersData, dp); 115 | delete request; 116 | } 117 | 118 | public int GetMembersData(const char[] data, any dp) { 119 | Handle hJson = json_load(data); 120 | Handle hData = view_as(dp); 121 | DiscordBot bot = view_as(json_object_get(hData, "bot")); 122 | 123 | Handle fwd = view_as(JsonObjectGetInt(hData, "callback")); 124 | 125 | char guild[32]; 126 | JsonObjectGetString(hData, "guild", guild, sizeof(guild)); 127 | 128 | if(fwd != null) { 129 | Call_StartForward(fwd); 130 | Call_PushCell(bot); 131 | Call_PushString(guild); 132 | Call_PushCell(hJson); 133 | Call_Finish(); 134 | } 135 | 136 | delete bot; 137 | if(JsonObjectGetBool(hData, "autoPaginate")) { 138 | int size = json_array_size(hJson); 139 | int limit = JsonObjectGetInt(hData, "limit"); 140 | if(limit == size) { 141 | Handle hLast = json_array_get(hJson, size - 1); 142 | char lastID[32]; 143 | json_string_value(hLast, lastID, sizeof(lastID)); 144 | delete hJson; 145 | delete hLast; 146 | 147 | json_object_set_new(hData, "afterID", json_string(lastID)); 148 | GetMembers(hData); 149 | return; 150 | } 151 | } 152 | 153 | delete hJson; 154 | delete hData; 155 | delete fwd; 156 | } -------------------------------------------------------------------------------- /scripting/include/sourcecomms.inc: -------------------------------------------------------------------------------- 1 | // ************************************************************************* 2 | // This file is part of SourceBans++. 3 | // 4 | // Copyright (C) 2014-2019 SourceBans++ Dev Team 5 | // 6 | // SourceBans++ is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, per version 3 of the License. 9 | // 10 | // SourceBans++ is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with SourceBans++. If not, see . 17 | // 18 | // This file based off work(s) covered by the following copyright(s): 19 | // 20 | // SourceComms 0.9.266 21 | // Copyright (C) 2013-2014 Alexandr Duplishchev 22 | // Licensed under GNU GPL version 3, or later. 23 | // Page: - 24 | // 25 | // ************************************************************************* 26 | 27 | #if defined _sourcecomms_included 28 | #endinput 29 | #endif 30 | #define _sourcecomms_included 31 | 32 | /** 33 | * @section Int definitions for punishments types. 34 | */ 35 | 36 | #define TYPE_MUTE 1 /**< Voice Mute */ 37 | #define TYPE_GAG 2 /**< Gag (text chat) */ 38 | #define TYPE_SILENCE 3 /**< Silence (mute + gag) */ 39 | #define TYPE_UNMUTE 4 /**< Voice Unmute*/ 40 | #define TYPE_UNGAG 5 /**< Ungag*/ 41 | #define TYPE_UNSILENCE 6 /**< Unsilence */ 42 | #define TYPE_TEMP_UNMUTE 14 /**< Temp mute removed */ 43 | #define TYPE_TEMP_UNGAG 15 /**< Temp gag removed */ 44 | #define TYPE_TEMP_UNSILENCE 16 /**< Temp silence removed */ 45 | 46 | /* Punishments types */ 47 | enum bType { 48 | bNot = 0, // Player chat or voice is not blocked 49 | bSess, // ... blocked for player session (until reconnect) 50 | bTime, // ... blocked for some time 51 | bPerm // ... permanently blocked 52 | } 53 | 54 | /** 55 | * Sets a client's mute state. 56 | * 57 | * @param client Client index. 58 | * @param muteState True to mute client, false to unmute. 59 | * -------------------------------------Parameters below this line are used only for muteState=true------------------------------------- 60 | * ----------------------------------for muteState=false these parameters are ignored (saveToDB=false)---------------------------------- 61 | * @param muteLength Length of punishment in minutes. Value < 0 muting client for session. Permanent (0) is not allowed at this time. 62 | * @param saveToDB If true, punishment will be saved in database. 63 | * @param reason Reason for punishment. 64 | * @return True if this caused a change in mute state, false otherwise. 65 | */ 66 | native bool SourceComms_SetClientMute(int client, bool muteState, int muteLength = -1, bool saveToDB = false, const char[] reason = "Muted through natives"); 67 | 68 | /** 69 | * Sets a client's gag state. 70 | * 71 | * @param client Client index. 72 | * @param gagState True to gag client, false to ungag. 73 | * --------------------------------------Parameters below this line are used only for gagState=true-------------------------------------- 74 | * -----------------------------------for gagState=false these parameters are ignored (saveToDB=false)----------------------------------- 75 | * @param gagLength Length of punishment in minutes. Value < 0 gagging client for session. Permanent (0) is not allowed at this time. 76 | * @param saveToDB If true, punishment will be saved in database. 77 | * @param reason Reason for punishment. 78 | * @return True if this caused a change in gag state, false otherwise. 79 | */ 80 | native bool SourceComms_SetClientGag(int client, bool gagState, int gagLength = -1, bool saveToDB = false, const char[] reason = "Gagged through natives"); 81 | 82 | /** 83 | * Returns the client's mute type 84 | * 85 | * @param client The client index of the player to check mute status 86 | * @return The client's current mute type index (see enum bType in the begin). 87 | */ 88 | native bType SourceComms_GetClientMuteType(int client); 89 | 90 | 91 | /** 92 | * Returns the client's gag type 93 | * 94 | * @param client The client index of the player to check gag status 95 | * @return The client's current gag type index (see enum bType in the begin). 96 | */ 97 | native bType SourceComms_GetClientGagType(int client); 98 | 99 | /** 100 | * Called when added communication block for player. 101 | * 102 | * @param client The client index of the admin who is blocking the client. 103 | * @param target The client index of the player to blocked. 104 | * @param time The time to blocked the player for (in minutes, 0 = permanent). 105 | * @param type The type of block. See section "Int definitions for punishments types". 106 | * @param reason The reason to block the player. 107 | */ 108 | forward void SourceComms_OnBlockAdded(int client, int target, int time, int type, char[] reason); 109 | 110 | public SharedPlugin __pl_sourcecomms = 111 | { 112 | name = "sourcecomms++", 113 | file = "sbpp_comms.smx", 114 | #if defined REQUIRE_PLUGIN 115 | required = 1 116 | #else 117 | required = 0 118 | #endif 119 | }; 120 | 121 | public void __pl_sourcecomms_SetNTVOptional() 122 | { 123 | MarkNativeAsOptional("SourceComms_SetClientMute"); 124 | MarkNativeAsOptional("SourceComms_SetClientGag"); 125 | MarkNativeAsOptional("SourceComms_GetClientMuteType"); 126 | MarkNativeAsOptional("SourceComms_GetClientGagType"); 127 | 128 | } -------------------------------------------------------------------------------- /scripting/include/discord/bot.inc: -------------------------------------------------------------------------------- 1 | methodmap DiscordBot < StringMap { 2 | public DiscordBot(const char[] token) { 3 | Handle json = json_object(); 4 | json_object_set_new(json, "token", json_string(token)); 5 | 6 | return view_as(json); 7 | } 8 | 9 | public void StopListening() { 10 | json_object_del(this, "listeningChannels"); 11 | } 12 | 13 | property float MessageCheckInterval { 14 | public get() { 15 | return JsonObjectGetFloat(this, "messageInterval", 3.0); 16 | } 17 | public set(float value) { 18 | json_object_set_new(this, "messageInterval", json_real(value)); 19 | } 20 | } 21 | 22 | public native void StartTimer(DiscordChannel Channel, OnChannelMessage fCallback); 23 | 24 | /** 25 | * Retrieves a list of Channels the bot is listening to for messages 26 | */ 27 | public Handle GetListeningChannels() { 28 | return json_object_get(this, "listeningChannels"); 29 | } 30 | 31 | /** 32 | * Checks if the bot is listening to channel for messages 33 | * @param DiscordChannel Channel 34 | */ 35 | public bool IsListeningToChannel(DiscordChannel Channel) { 36 | char id[32]; 37 | Channel.GetID(id, sizeof(id)); 38 | 39 | Handle hChannels = this.GetListeningChannels(); 40 | if(hChannels == null) return false; 41 | 42 | for(int i = 0; i < json_array_size(hChannels); i++) { 43 | DiscordChannel tempChannel = view_as(json_array_get(hChannels, i)); 44 | static char tempID[32]; 45 | tempChannel.GetID(tempID, sizeof(tempID)); 46 | if(StrEqual(id, tempID, false)) { 47 | delete tempChannel; 48 | delete hChannels; 49 | return true; 50 | } 51 | delete tempChannel; 52 | } 53 | delete hChannels; 54 | return false; 55 | } 56 | 57 | /** 58 | * Checks if the bot is listening to channel for messages 59 | * @param DiscordChannel Channel 60 | */ 61 | public bool IsListeningToChannelID(const char[] id) { 62 | Handle hChannels = this.GetListeningChannels(); 63 | if(hChannels == null) return false; 64 | 65 | for(int i = 0; i < json_array_size(hChannels); i++) { 66 | DiscordChannel tempChannel = view_as(json_array_get(hChannels, i)); 67 | static char tempID[32]; 68 | tempChannel.GetID(tempID, sizeof(tempID)); 69 | if(StrEqual(id, tempID, false)) { 70 | delete tempChannel; 71 | delete hChannels; 72 | return true; 73 | } 74 | delete tempChannel; 75 | } 76 | delete hChannels; 77 | return false; 78 | } 79 | 80 | /** 81 | * Stops the bot from listening to that channel for messages 82 | * @param DiscordChannel Channel 83 | */ 84 | public void StopListeningToChannel(DiscordChannel Channel) { 85 | char id[32]; 86 | Channel.GetID(id, sizeof(id)); 87 | 88 | Handle channels = this.GetListeningChannels(); 89 | if(channels == null) return; 90 | 91 | for(int i = 0; i < json_array_size(channels); i++) { 92 | DiscordChannel tempChannel = view_as(json_array_get(channels, i)); 93 | static char tempID[32]; 94 | tempChannel.GetID(tempID, sizeof(tempID)); 95 | if(StrEqual(id, tempID, false)) { 96 | json_array_remove(channels, i); 97 | i--; 98 | delete tempChannel; 99 | } 100 | } 101 | delete channels; 102 | } 103 | 104 | /** 105 | * Stops the bot from listening to that channel id for messages 106 | * @param DiscordChannel Channel 107 | */ 108 | public void StopListeningToChannelID(const char[] id) { 109 | Handle channels = this.GetListeningChannels(); 110 | if(channels == null) return; 111 | 112 | for(int i = 0; i < json_array_size(channels); i++) { 113 | DiscordChannel tempChannel = view_as(json_array_get(channels, i)); 114 | static char tempID[32]; 115 | tempChannel.GetID(tempID, sizeof(tempID)); 116 | if(StrEqual(id, tempID, false)) { 117 | json_array_remove(channels, i); 118 | i--; 119 | delete tempChannel; 120 | } 121 | } 122 | delete channels; 123 | } 124 | 125 | public DiscordChannel GetListeningChannelByID(const char[] id) { 126 | Handle channels = this.GetListeningChannels(); 127 | if(channels == null) return null; 128 | 129 | for(int i = 0; i < json_array_size(channels); i++) { 130 | DiscordChannel tempChannel = view_as(json_array_get(channels, i)); 131 | static char tempID[32]; 132 | tempChannel.GetID(tempID, sizeof(tempID)); 133 | if(StrEqual(id, tempID, false)) { 134 | delete channels; 135 | return tempChannel; 136 | } 137 | } 138 | delete channels; 139 | return null; 140 | } 141 | 142 | /** 143 | * Start listening to the channel for messages. 144 | * The Channel handle is duplicated. Feel free to close yours. 145 | * @param DiscordChannel Channel 146 | */ 147 | public void StartListeningToChannel(DiscordChannel Channel, OnChannelMessage fCallback) { 148 | if(this.IsListeningToChannel(Channel)) return; 149 | 150 | Handle channels = this.GetListeningChannels(); 151 | 152 | if(channels == null) { 153 | channels = json_array(); 154 | json_object_set(this, "listeningChannels", channels); 155 | } 156 | 157 | json_array_append(channels, Channel); 158 | 159 | //Handle fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_String, Param_String, Param_String, Param_String, Param_String, Param_Cell); 160 | //AddToForward(fForward, GetMyHandle(), callback); 161 | 162 | this.StartTimer(Channel, fCallback); 163 | } 164 | 165 | 166 | public native void AddReactionID(const char[] channel, const char[] messageid, const char[] emoji); 167 | 168 | public void AddReaction(DiscordChannel channel, const char[] messageid, const char[] emoji) { 169 | char channelid[64]; 170 | channel.GetID(channelid, sizeof(channelid)); 171 | this.AddReactionID(channelid, messageid, emoji); 172 | } 173 | 174 | public native void DeleteReactionID(const char[] channel, const char[] messageid, const char[] emoji, const char[] user); 175 | 176 | public void DeleteReaction(DiscordChannel channel, const char[] messageid, const char[] emoji, const char[] user) { 177 | char chid[64]; 178 | channel.GetID(chid, sizeof(chid)); 179 | this.DeleteReactionID(chid, messageid, emoji, user); 180 | } 181 | 182 | public void DeleteReactionSelf(DiscordChannel channel, const char[] messageid, const char[] emoji) { 183 | this.DeleteReaction(channel, messageid, emoji, "@me"); 184 | } 185 | public void DeleteReactionAll(DiscordChannel channel, const char[] messageid, const char[] emoji) { 186 | this.DeleteReaction(channel, messageid, emoji, "@all"); 187 | } 188 | 189 | public void DeleteReactionSelfID(const char[] channel, const char[] messageid, const char[] emoji) { 190 | this.DeleteReactionID(channel, messageid, emoji, "@me"); 191 | } 192 | public void DeleteReactionAllID(const char[] channel, const char[] messageid, const char[] emoji) { 193 | this.DeleteReactionID(channel, messageid, emoji, "@all"); 194 | } 195 | 196 | public native void GetReactionID(const char[] channel, const char[] messageid, const char[] emoji, OnGetReactions fCallback=INVALID_FUNCTION, any data=0); 197 | 198 | public void GetReaction(DiscordChannel channel, const char[] messageid, const char[] emoji, OnGetReactions fCallback=INVALID_FUNCTION, any data=0) { 199 | char id[64]; 200 | channel.GetID(id, sizeof(id)); 201 | this.GetReactionID(id, messageid, emoji, fCallback, data); 202 | } 203 | 204 | public native void GetToken(char[] token, int maxlength); 205 | 206 | public native void SendMessage(DiscordChannel channel, char[] message, OnMessageSent fCallback=INVALID_FUNCTION, any data=0); 207 | 208 | public native void SendMessageToChannelID(char[] channel, char[] message, OnMessageSent fCallback=INVALID_FUNCTION, any data=0); 209 | 210 | public native void DeleteMessageID(char[] channel, char[] message, OnMessageDeleted fCallback=INVALID_FUNCTION, any data=0); 211 | public native void DeleteMessage(DiscordChannel channel, DiscordMessage message, OnMessageDeleted fCallback=INVALID_FUNCTION, any data=0); 212 | 213 | 214 | public native void GetGuilds(DiscordGuildsRetrieve fCallback = INVALID_FUNCTION, DiscordGuildsRetrievedAll fCallbackAll = INVALID_FUNCTION, any data=0); 215 | 216 | public native void GetGuildChannels(char[] guild, DiscordGuildChannelsRetrieve fCallback = INVALID_FUNCTION, DiscordGuildChannelsRetrieveAll fCallbackAll = INVALID_FUNCTION, any data=0); 217 | 218 | /** 219 | * ATM takes guild id, hopefully later on i will implement guild objects. 220 | * Limit is from 1-1000 221 | */ 222 | public native void GetGuildMembers(char[] guild, OnGetMembers fCallback, int limit=250, char[] afterUserID=""); 223 | 224 | /** 225 | * Same as above but displays ALL members, paginating automatically. 226 | * perPage is how many it should display per callback. 1-1000 227 | */ 228 | public native void GetGuildMembersAll(char[] guild, OnGetMembers fCallback, int perPage=250, char[] afterUserID=""); 229 | 230 | public native void GetGuildRoles(char[] guild, DiscordGuildGetRoles fCallback, any data); 231 | }; 232 | -------------------------------------------------------------------------------- /scripting/discord/reactions.sp: -------------------------------------------------------------------------------- 1 | public int Native_DiscordBot_AddReaction(Handle plugin, int numParams) { 2 | DiscordBot bot = GetNativeCell(1); 3 | 4 | char channel[32]; 5 | GetNativeString(2, channel, sizeof(channel)); 6 | 7 | char msgid[64]; 8 | GetNativeString(3, msgid, sizeof(msgid)); 9 | 10 | char emoji[128]; 11 | GetNativeString(4, emoji, sizeof(emoji)); 12 | 13 | AddReaction(bot, channel, msgid, emoji); 14 | } 15 | 16 | public int Native_DiscordBot_DeleteReaction(Handle plugin, int numParams) { 17 | DiscordBot bot = GetNativeCell(1); 18 | 19 | char channel[32]; 20 | GetNativeString(2, channel, sizeof(channel)); 21 | 22 | char msgid[64]; 23 | GetNativeString(3, msgid, sizeof(msgid)); 24 | 25 | char emoji[128]; 26 | GetNativeString(4, emoji, sizeof(emoji)); 27 | 28 | char user[128]; 29 | GetNativeString(5, user, sizeof(user)); 30 | 31 | DeleteReaction(bot, channel, msgid, emoji, user); 32 | } 33 | 34 | public int Native_DiscordBot_GetReaction(Handle plugin, int numParams) { 35 | DiscordBot bot = GetNativeCell(1); 36 | 37 | char channel[32]; 38 | GetNativeString(2, channel, sizeof(channel)); 39 | 40 | char msgid[64]; 41 | GetNativeString(3, msgid, sizeof(msgid)); 42 | 43 | char emoji[128]; 44 | GetNativeString(4, emoji, sizeof(emoji)); 45 | 46 | OnGetReactions fCallback = GetNativeCell(5); 47 | Handle fForward = null; 48 | if(fCallback != INVALID_FUNCTION) { 49 | fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_String, Param_String, Param_String, Param_String, Param_Cell); 50 | AddToForward(fForward, plugin, fCallback); 51 | } 52 | 53 | any data = GetNativeCell(6); 54 | 55 | GetReaction(bot, channel, msgid, emoji, fForward, data); 56 | } 57 | 58 | ///channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me 59 | public void AddReaction(DiscordBot bot, char[] channel, char[] messageid, char[] emoji) { 60 | char url[256]; 61 | FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s/@me", channel, messageid, emoji); 62 | 63 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodPUT, null, AddReactionReceiveData); 64 | 65 | DataPack dp = new DataPack(); 66 | WritePackCell(dp, bot); 67 | WritePackString(dp, channel); 68 | WritePackString(dp, messageid); 69 | WritePackString(dp, emoji); 70 | 71 | if(request == dp) { 72 | CreateTimer(2.0, AddReactionDelayed, dp); 73 | return; 74 | } 75 | 76 | char route[128]; 77 | FormatEx(route, sizeof(route), "channels/%s/messages/msgid/reactions", channel); 78 | 79 | SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(route)); 80 | 81 | DiscordSendRequest(request, url); 82 | } 83 | 84 | public Action AddReactionDelayed(Handle timer, any data) { 85 | DataPack dp = view_as(data); 86 | 87 | DiscordBot bot = ReadPackCell(dp); 88 | char channel[64]; 89 | char messageid[64]; 90 | char emoji[64]; 91 | ReadPackString(dp, channel, sizeof(channel)); 92 | ReadPackString(dp, messageid, sizeof(messageid)); 93 | ReadPackString(dp, emoji, sizeof(emoji)); 94 | delete dp; 95 | 96 | AddReaction(bot, channel, messageid, emoji); 97 | } 98 | 99 | public AddReactionReceiveData(Handle request, bool failure, int offset, int statuscode, any data) { 100 | if(failure || statuscode != 204) { 101 | if(statuscode == 429 || statuscode == 500) { 102 | 103 | DataPack dp = view_as(data); 104 | 105 | DiscordBot bot = ReadPackCell(dp); 106 | char channel[64]; 107 | char messageid[64]; 108 | char emoji[64]; 109 | ReadPackString(dp, channel, sizeof(channel)); 110 | ReadPackString(dp, messageid, sizeof(messageid)); 111 | ReadPackString(dp, emoji, sizeof(emoji)); 112 | delete dp; 113 | 114 | AddReaction(bot, channel, messageid, emoji); 115 | 116 | delete request; 117 | return; 118 | } 119 | LogError("[DISCORD] Couldn't Add Reaction - Fail %i %i", failure, statuscode); 120 | delete request; 121 | delete view_as(data); 122 | delete view_as(data); 123 | return; 124 | } 125 | delete request; 126 | delete view_as(data); 127 | } 128 | 129 | ///channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id} 130 | public void DeleteReaction(DiscordBot bot, char[] channel, char[] messageid, char[] emoji, char[] userid) { 131 | char url[256]; 132 | 133 | if(StrEqual(userid, "@all")) { 134 | FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s", channel, messageid, emoji); 135 | }else { 136 | FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s/%s", channel, messageid, emoji, userid); 137 | } 138 | 139 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodDELETE, null, DeleteReactionReceiveData); 140 | 141 | DataPack dp = new DataPack(); 142 | WritePackCell(dp, bot); 143 | WritePackString(dp, channel); 144 | WritePackString(dp, messageid); 145 | WritePackString(dp, emoji); 146 | WritePackString(dp, userid); 147 | 148 | if(request == dp) { 149 | CreateTimer(2.0, DeleteReactionDelayed, dp); 150 | return; 151 | } 152 | 153 | char route[128]; 154 | FormatEx(route, sizeof(route), "channels/%s/messages/msgid/reactions", channel); 155 | 156 | SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(route)); 157 | 158 | DiscordSendRequest(request, url); 159 | } 160 | 161 | public Action DeleteReactionDelayed(Handle timer, any data) { 162 | DataPack dp = view_as(data); 163 | 164 | DiscordBot bot = ReadPackCell(dp); 165 | char channel[64]; 166 | char messageid[64]; 167 | char emoji[64]; 168 | char userid[64]; 169 | ReadPackString(dp, channel, sizeof(channel)); 170 | ReadPackString(dp, messageid, sizeof(messageid)); 171 | ReadPackString(dp, emoji, sizeof(emoji)); 172 | ReadPackString(dp, userid, sizeof(userid)); 173 | delete dp; 174 | 175 | DeleteReaction(bot, channel, messageid, emoji, userid); 176 | } 177 | 178 | public DeleteReactionReceiveData(Handle request, bool failure, int offset, int statuscode, any data) { 179 | if(failure || statuscode != 204) { 180 | if(statuscode == 429 || statuscode == 500) { 181 | 182 | DataPack dp = view_as(data); 183 | 184 | DiscordBot bot = ReadPackCell(dp); 185 | char channel[64]; 186 | char messageid[64]; 187 | char emoji[64]; 188 | char userid[64]; 189 | ReadPackString(dp, channel, sizeof(channel)); 190 | ReadPackString(dp, messageid, sizeof(messageid)); 191 | ReadPackString(dp, emoji, sizeof(emoji)); 192 | ReadPackString(dp, userid, sizeof(userid)); 193 | delete dp; 194 | 195 | DeleteReaction(bot, channel, messageid, emoji, userid); 196 | 197 | delete request; 198 | return; 199 | } 200 | LogError("[DISCORD] Couldn't Delete Reaction - Fail %i %i", failure, statuscode); 201 | delete request; 202 | delete view_as(data); 203 | return; 204 | } 205 | delete request; 206 | delete view_as(data); 207 | } 208 | 209 | public void GetReaction(DiscordBot bot, char[] channel, char[] messageid, char[] emoji, Handle fForward, any data) { 210 | char url[256]; 211 | FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s", channel, messageid, emoji); 212 | 213 | Handle request = PrepareRequest(bot, url, k_EHTTPMethodGET, null, GetReactionReceiveData); 214 | 215 | DataPack dp = new DataPack(); 216 | WritePackCell(dp, bot); 217 | WritePackString(dp, channel); 218 | WritePackString(dp, messageid); 219 | WritePackString(dp, emoji); 220 | WritePackCell(dp, fForward); 221 | WritePackCell(dp, data); 222 | 223 | if(request == dp) { 224 | CreateTimer(2.0, GetReactionDelayed, dp); 225 | return; 226 | } 227 | 228 | char route[128]; 229 | FormatEx(route, sizeof(route), "channels/%s/messages/msgid/reactions", channel); 230 | 231 | SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(route)); 232 | 233 | DiscordSendRequest(request, url); 234 | } 235 | 236 | public Action GetReactionDelayed(Handle timer, any data) { 237 | DataPack dp = view_as(data); 238 | 239 | DiscordBot bot = ReadPackCell(dp); 240 | char channel[64]; 241 | char messageid[64]; 242 | char emoji[64]; 243 | ReadPackString(dp, channel, sizeof(channel)); 244 | ReadPackString(dp, messageid, sizeof(messageid)); 245 | ReadPackString(dp, emoji, sizeof(emoji)); 246 | Handle fForward = ReadPackCell(dp); 247 | any addData = ReadPackCell(dp); 248 | delete dp; 249 | 250 | GetReaction(bot, channel, messageid, emoji, fForward, addData); 251 | } 252 | 253 | public GetReactionReceiveData(Handle request, bool failure, int offset, int statuscode, any data) { 254 | if(failure || statuscode != 204) { 255 | if(statuscode == 429 || statuscode == 500) { 256 | 257 | DataPack dp = view_as(data); 258 | 259 | DiscordBot bot = ReadPackCell(dp); 260 | char channel[64]; 261 | char messageid[64]; 262 | char emoji[64]; 263 | ReadPackString(dp, channel, sizeof(channel)); 264 | ReadPackString(dp, messageid, sizeof(messageid)); 265 | ReadPackString(dp, emoji, sizeof(emoji)); 266 | Handle fForward = ReadPackCell(dp); 267 | any addData = ReadPackCell(dp); 268 | delete dp; 269 | 270 | GetReaction(bot, channel, messageid, emoji, fForward, addData); 271 | 272 | delete request; 273 | return; 274 | } 275 | LogError("[DISCORD] Couldn't Delete Reaction - Fail %i %i", failure, statuscode); 276 | delete request; 277 | delete view_as(data); 278 | return; 279 | } 280 | 281 | SteamWorks_GetHTTPResponseBodyCallback(request, GetReactionsData, data); 282 | 283 | delete request; 284 | } 285 | 286 | public int GetReactionsData(const char[] data, any datapack) { 287 | DataPack dp = view_as(datapack); 288 | 289 | DiscordBot bot = ReadPackCell(dp); 290 | char channel[64]; 291 | char messageid[64]; 292 | char emoji[64]; 293 | ReadPackString(dp, channel, sizeof(channel)); 294 | ReadPackString(dp, messageid, sizeof(messageid)); 295 | ReadPackString(dp, emoji, sizeof(emoji)); 296 | Handle fForward = ReadPackCell(dp); 297 | any addData = ReadPackCell(dp); 298 | delete dp; 299 | 300 | Handle hJson = json_load(data); 301 | 302 | ArrayList alUsers = new ArrayList(); 303 | 304 | if(json_is_array(hJson)) { 305 | for(int i = 0; i < json_array_size(hJson); i++) { 306 | DiscordUser user = view_as(json_array_get(hJson, i)); 307 | alUsers.Push(user); 308 | } 309 | } 310 | delete hJson; 311 | 312 | if(fForward != null) { 313 | Call_StartForward(fForward); 314 | Call_PushCell(bot); 315 | Call_PushCell(alUsers); 316 | Call_PushString(channel); 317 | Call_PushString(messageid); 318 | Call_PushString(emoji); 319 | Call_PushCell(addData); 320 | Call_Finish(); 321 | } 322 | 323 | for(int i = 0; i < alUsers.Length; i++) { 324 | DiscordUser user = alUsers.Get(i); 325 | delete user; 326 | } 327 | delete alUsers; 328 | } -------------------------------------------------------------------------------- /scripting/discord_api.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | 3 | #define PLUGIN_VERSION "0.1.103" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "discord/DiscordRequest.sp" 11 | #include "discord/SendMessage.sp" 12 | #include "discord/GetGuilds.sp" 13 | #include "discord/GetGuildChannels.sp" 14 | #include "discord/ListenToChannel.sp" 15 | #include "discord/SendWebHook.sp" 16 | #include "discord/reactions.sp" 17 | #include "discord/UserObject.sp" 18 | #include "discord/MessageObject.sp" 19 | #include "discord/GuildMembers.sp" 20 | #include "discord/GuildRole.sp" 21 | #include "discord/deletemessage.sp" 22 | 23 | //For rate limitation 24 | Handle hRateLimit = null; 25 | Handle hRateReset = null; 26 | Handle hRateLeft = null; 27 | 28 | public Plugin myinfo = { 29 | name = "Discord API", 30 | author = "Deathknife", 31 | description = "", 32 | version = PLUGIN_VERSION, 33 | url = "" 34 | }; 35 | 36 | public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { 37 | CreateNative("DiscordBot.GetToken", Native_DiscordBot_Token_Get); 38 | 39 | //SendMessage.sp 40 | CreateNative("DiscordBot.SendMessage", Native_DiscordBot_SendMessage); 41 | CreateNative("DiscordBot.SendMessageToChannelID", Native_DiscordBot_SendMessageToChannel); 42 | CreateNative("DiscordChannel.SendMessage", Native_DiscordChannel_SendMessage); 43 | 44 | //deletemessage.sp 45 | CreateNative("DiscordBot.DeleteMessageID", Native_DiscordBot_DeleteMessageID); 46 | CreateNative("DiscordBot.DeleteMessage", Native_DiscordBot_DeleteMessage); 47 | 48 | //ListenToChannel.sp 49 | CreateNative("DiscordBot.StartTimer", Native_DiscordBot_StartTimer); 50 | 51 | //GetGuilds.sp 52 | CreateNative("DiscordBot.GetGuilds", Native_DiscordBot_GetGuilds); 53 | //GetGuildChannels.sp 54 | CreateNative("DiscordBot.GetGuildChannels", Native_DiscordBot_GetGuildChannels); 55 | //GuildRole.sp 56 | CreateNative("DiscordBot.GetGuildRoles", Native_DiscordBot_GetGuildRoles); 57 | 58 | //reactions.sp 59 | CreateNative("DiscordBot.AddReactionID", Native_DiscordBot_AddReaction); 60 | CreateNative("DiscordBot.DeleteReactionID", Native_DiscordBot_DeleteReaction); 61 | CreateNative("DiscordBot.GetReactionID", Native_DiscordBot_GetReaction); 62 | 63 | //GuildMembers.sp 64 | CreateNative("DiscordBot.GetGuildMembers", Native_DiscordBot_GetGuildMembers); 65 | CreateNative("DiscordBot.GetGuildMembersAll", Native_DiscordBot_GetGuildMembersAll); 66 | 67 | //CreateNative("DiscordChannel.Destroy", Native_DiscordChannel_Destroy); 68 | 69 | //SendWebHook.sp 70 | CreateNative("DiscordWebHook.Send", Native_DiscordWebHook_Send); 71 | //CreateNative("DiscordWebHook.AddField", Native_DiscordWebHook_AddField); 72 | //CreateNative("DiscordWebHook.DeleteFields", Native_DiscordWebHook_DeleteFields); 73 | 74 | //UserObject.sp 75 | CreateNative("DiscordUser.GetID", Native_DiscordUser_GetID); 76 | CreateNative("DiscordUser.GetUsername", Native_DiscordUser_GetUsername); 77 | CreateNative("DiscordUser.GetDiscriminator", Native_DiscordUser_GetDiscriminator); 78 | CreateNative("DiscordUser.GetAvatar", Native_DiscordUser_GetAvatar); 79 | CreateNative("DiscordUser.IsVerified", Native_DiscordUser_IsVerified); 80 | CreateNative("DiscordUser.GetEmail", Native_DiscordUser_GetEmail); 81 | CreateNative("DiscordUser.IsBot", Native_DiscordUser_IsBot); 82 | 83 | //MessageObject.sp 84 | CreateNative("DiscordMessage.GetID", Native_DiscordMessage_GetID); 85 | CreateNative("DiscordMessage.IsPinned", Native_DiscordMessage_IsPinned); 86 | CreateNative("DiscordMessage.GetAuthor", Native_DiscordMessage_GetAuthor); 87 | CreateNative("DiscordMessage.GetContent", Native_DiscordMessage_GetContent); 88 | CreateNative("DiscordMessage.GetChannelID", Native_DiscordMessage_GetChannelID); 89 | 90 | RegPluginLibrary("discord-api"); 91 | 92 | return APLRes_Success; 93 | } 94 | 95 | public void OnPluginStart() { 96 | hRateLeft = new StringMap(); 97 | hRateReset = new StringMap(); 98 | hRateLimit = new StringMap(); 99 | } 100 | 101 | public int Native_DiscordBot_Token_Get(Handle plugin, int numParams) { 102 | DiscordBot bot = GetNativeCell(1); 103 | static char token[196]; 104 | JsonObjectGetString(bot, "token", token, sizeof(token)); 105 | SetNativeString(2, token, GetNativeCell(3)); 106 | } 107 | 108 | stock void BuildAuthHeader(Handle request, DiscordBot Bot) { 109 | static char buffer[256]; 110 | static char token[196]; 111 | JsonObjectGetString(Bot, "token", token, sizeof(token)); 112 | FormatEx(buffer, sizeof(buffer), "Bot %s", token); 113 | SteamWorks_SetHTTPRequestHeaderValue(request, "Authorization", buffer); 114 | } 115 | 116 | 117 | stock Handle PrepareRequest(DiscordBot bot, char[] url, EHTTPMethod method=k_EHTTPMethodGET, Handle hJson=null, SteamWorksHTTPDataReceived DataReceived = INVALID_FUNCTION, SteamWorksHTTPRequestCompleted RequestCompleted = INVALID_FUNCTION) { 118 | static char stringJson[16384]; 119 | stringJson[0] = '\0'; 120 | if(hJson != null) { 121 | json_dump(hJson, stringJson, sizeof(stringJson), 0, true); 122 | } 123 | 124 | //Format url 125 | static char turl[128]; 126 | FormatEx(turl, sizeof(turl), "https://discordapp.com/api/%s", url); 127 | 128 | Handle request = SteamWorks_CreateHTTPRequest(method, turl); 129 | if(request == null) { 130 | return null; 131 | } 132 | 133 | if(bot != null) { 134 | BuildAuthHeader(request, bot); 135 | } 136 | 137 | SteamWorks_SetHTTPRequestRawPostBody(request, "application/json; charset=UTF-8", stringJson, strlen(stringJson)); 138 | 139 | SteamWorks_SetHTTPRequestNetworkActivityTimeout(request, 30); 140 | 141 | if(RequestCompleted == INVALID_FUNCTION) { 142 | //I had some bugs previously where it wouldn't send request and return code 0 if I didn't set request completed. 143 | //This is just a safety then, my issue could have been something else and I will test more later on 144 | RequestCompleted = HTTPCompleted; 145 | } 146 | 147 | if(DataReceived == INVALID_FUNCTION) { 148 | //Need to close the request handle 149 | DataReceived = HTTPDataReceive; 150 | } 151 | 152 | SteamWorks_SetHTTPCallbacks(request, RequestCompleted, HeadersReceived, DataReceived); 153 | if(hJson != null) delete hJson; 154 | 155 | return request; 156 | } 157 | 158 | stock Handle PrepareRequestRaw(DiscordBot bot, char[] url, EHTTPMethod method=k_EHTTPMethodGET, Handle hJson=null, SteamWorksHTTPDataReceived DataReceived = INVALID_FUNCTION, SteamWorksHTTPRequestCompleted RequestCompleted = INVALID_FUNCTION) { 159 | static char stringJson[16384]; 160 | stringJson[0] = '\0'; 161 | if(hJson != null) { 162 | json_dump(hJson, stringJson, sizeof(stringJson), 0, true); 163 | } 164 | 165 | Handle request = SteamWorks_CreateHTTPRequest(method, url); 166 | if(request == null) { 167 | return null; 168 | } 169 | 170 | if(bot != null) { 171 | BuildAuthHeader(request, bot); 172 | } 173 | 174 | SteamWorks_SetHTTPRequestRawPostBody(request, "application/json; charset=UTF-8", stringJson, strlen(stringJson)); 175 | 176 | SteamWorks_SetHTTPRequestNetworkActivityTimeout(request, 30); 177 | 178 | if(RequestCompleted == INVALID_FUNCTION) { 179 | //I had some bugs previously where it wouldn't send request and return code 0 if I didn't set request completed. 180 | //This is just a safety then, my issue could have been something else and I will test more later on 181 | RequestCompleted = HTTPCompleted; 182 | } 183 | 184 | if(DataReceived == INVALID_FUNCTION) { 185 | //Need to close the request handle 186 | DataReceived = HTTPDataReceive; 187 | } 188 | 189 | SteamWorks_SetHTTPCallbacks(request, RequestCompleted, HeadersReceived, DataReceived); 190 | if(hJson != null) delete hJson; 191 | 192 | return request; 193 | } 194 | 195 | public int HTTPCompleted(Handle request, bool failure, bool requestSuccessful, EHTTPStatusCode statuscode, any data, any data2) { 196 | } 197 | 198 | public int HTTPDataReceive(Handle request, bool failure, int offset, int statuscode, any dp) { 199 | delete request; 200 | } 201 | 202 | public int HeadersReceived(Handle request, bool failure, any data, any datapack) { 203 | DataPack dp = view_as(datapack); 204 | if(failure) { 205 | delete dp; 206 | return; 207 | } 208 | 209 | char xRateLimit[16]; 210 | char xRateLeft[16]; 211 | char xRateReset[32]; 212 | 213 | bool exists = false; 214 | 215 | exists = SteamWorks_GetHTTPResponseHeaderValue(request, "X-RateLimit-Limit", xRateLimit, sizeof(xRateLimit)); 216 | exists = SteamWorks_GetHTTPResponseHeaderValue(request, "X-RateLimit-Remaining", xRateLeft, sizeof(xRateLeft)); 217 | exists = SteamWorks_GetHTTPResponseHeaderValue(request, "X-RateLimit-Reset", xRateReset, sizeof(xRateReset)); 218 | 219 | //Get url 220 | char route[128]; 221 | ResetPack(dp); 222 | ReadPackString(dp, route, sizeof(route)); 223 | delete dp; 224 | 225 | int reset = StringToInt(xRateReset); 226 | if(reset > GetTime() + 3) { 227 | reset = GetTime() + 3; 228 | } 229 | 230 | if(exists) { 231 | SetTrieValue(hRateReset, route, reset); 232 | SetTrieValue(hRateLeft, route, StringToInt(xRateLeft)); 233 | SetTrieValue(hRateLimit, route, StringToInt(xRateLimit)); 234 | }else { 235 | SetTrieValue(hRateReset, route, -1); 236 | SetTrieValue(hRateLeft, route, -1); 237 | SetTrieValue(hRateLimit, route, -1); 238 | } 239 | } 240 | 241 | /* 242 | This is rate limit imposing for per-route basis. Doesn't support global limit yet. 243 | */ 244 | public void DiscordSendRequest(Handle request, const char[] route) { 245 | //Check for reset 246 | int time = GetTime(); 247 | int resetTime; 248 | 249 | int defLimit = 0; 250 | if(!GetTrieValue(hRateLimit, route, defLimit)) { 251 | defLimit = 1; 252 | } 253 | 254 | bool exists = GetTrieValue(hRateReset, route, resetTime); 255 | 256 | if(!exists) { 257 | SetTrieValue(hRateReset, route, GetTime() + 5); 258 | SetTrieValue(hRateLeft, route, defLimit - 1); 259 | SteamWorks_SendHTTPRequest(request); 260 | return; 261 | } 262 | 263 | if(time == -1) { 264 | //No x-rate-limit send 265 | SteamWorks_SendHTTPRequest(request); 266 | return; 267 | } 268 | 269 | if(time > resetTime) { 270 | SetTrieValue(hRateLeft, route, defLimit - 1); 271 | SteamWorks_SendHTTPRequest(request); 272 | return; 273 | }else { 274 | int left; 275 | GetTrieValue(hRateLeft, route, left); 276 | if(left == 0) { 277 | float remaining = float(resetTime) - float(time) + 1.0; 278 | Handle dp = new DataPack(); 279 | WritePackCell(dp, request); 280 | WritePackString(dp, route); 281 | CreateTimer(remaining, SendRequestAgain, dp); 282 | }else { 283 | left--; 284 | SetTrieValue(hRateLeft, route, left); 285 | SteamWorks_SendHTTPRequest(request); 286 | } 287 | } 288 | } 289 | 290 | public Handle UrlToDP(char[] url) { 291 | DataPack dp = new DataPack(); 292 | WritePackString(dp, url); 293 | return dp; 294 | } 295 | 296 | public Action SendRequestAgain(Handle timer, any dp) { 297 | ResetPack(dp); 298 | Handle request = ReadPackCell(dp); 299 | char route[128]; 300 | ReadPackString(dp, route, sizeof(route)); 301 | delete view_as(dp); 302 | DiscordSendRequest(request, route); 303 | } 304 | 305 | stock bool RenameJsonObject(Handle hJson, char[] key, char[] toKey) { 306 | Handle hObject = json_object_get(hJson, key); 307 | if(hObject != null) { 308 | json_object_set_new(hJson, toKey, hObject); 309 | json_object_del(hJson, key); 310 | return true; 311 | } 312 | return false; 313 | } -------------------------------------------------------------------------------- /scripting/include/SteamWorks.inc: -------------------------------------------------------------------------------- 1 | #if defined _SteamWorks_Included 2 | #endinput 3 | #endif 4 | #define _SteamWorks_Included 5 | 6 | /* results from UserHasLicenseForApp */ 7 | enum EUserHasLicenseForAppResult 8 | { 9 | k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app 10 | k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app 11 | k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated 12 | }; 13 | 14 | /* General result codes */ 15 | enum EResult 16 | { 17 | k_EResultOK = 1, // success 18 | k_EResultFail = 2, // generic failure 19 | k_EResultNoConnection = 3, // no/failed network connection 20 | // k_EResultNoConnectionRetry = 4, // OBSOLETE - removed 21 | k_EResultInvalidPassword = 5, // password/ticket is invalid 22 | k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere 23 | k_EResultInvalidProtocolVer = 7, // protocol version is incorrect 24 | k_EResultInvalidParam = 8, // a parameter is incorrect 25 | k_EResultFileNotFound = 9, // file was not found 26 | k_EResultBusy = 10, // called method busy - action not taken 27 | k_EResultInvalidState = 11, // called object was in an invalid state 28 | k_EResultInvalidName = 12, // name is invalid 29 | k_EResultInvalidEmail = 13, // email is invalid 30 | k_EResultDuplicateName = 14, // name is not unique 31 | k_EResultAccessDenied = 15, // access is denied 32 | k_EResultTimeout = 16, // operation timed out 33 | k_EResultBanned = 17, // VAC2 banned 34 | k_EResultAccountNotFound = 18, // account not found 35 | k_EResultInvalidSteamID = 19, // steamID is invalid 36 | k_EResultServiceUnavailable = 20, // The requested service is currently unavailable 37 | k_EResultNotLoggedOn = 21, // The user is not logged on 38 | k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party) 39 | k_EResultEncryptionFailure = 23, // Encryption or Decryption failed 40 | k_EResultInsufficientPrivilege = 24, // Insufficient privilege 41 | k_EResultLimitExceeded = 25, // Too much of a good thing 42 | k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes) 43 | k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired 44 | k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again 45 | k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time 46 | k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user 47 | k_EResultIPNotFound = 31, // IP address not found 48 | k_EResultPersistFailed = 32, // failed to write change to the data store 49 | k_EResultLockingFailed = 33, // failed to acquire access lock for this operation 50 | k_EResultLogonSessionReplaced = 34, 51 | k_EResultConnectFailed = 35, 52 | k_EResultHandshakeFailed = 36, 53 | k_EResultIOFailure = 37, 54 | k_EResultRemoteDisconnect = 38, 55 | k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested 56 | k_EResultBlocked = 40, // a user didn't allow it 57 | k_EResultIgnored = 41, // target is ignoring sender 58 | k_EResultNoMatch = 42, // nothing matching the request found 59 | k_EResultAccountDisabled = 43, 60 | k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now 61 | k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available 62 | k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin 63 | k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol. 64 | k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another. 65 | k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed. 66 | k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait 67 | k_EResultSuspended = 51, // Long running operation (content download) suspended/paused 68 | k_EResultCancelled = 52, // Operation canceled (typically by user: content download) 69 | k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable 70 | k_EResultDiskFull = 54, // Operation canceled - not enough disk space. 71 | k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed 72 | k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side 73 | k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account 74 | k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid 75 | k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first 76 | k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files 77 | k_EResultIllegalPassword = 61, // The requested new password is not legal 78 | k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer ) 79 | k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure 80 | k_EResultCannotUseOldPassword = 64, // The requested new password is not legal 81 | k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid 82 | k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent 83 | k_EResultHardwareNotCapableOfIPT = 67, // 84 | k_EResultIPTInitError = 68, // 85 | k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user 86 | k_EResultFacebookQueryError = 70, // Facebook query returned an error 87 | k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired 88 | k_EResultIPLoginRestrictionFailed = 72, 89 | k_EResultAccountLockedDown = 73, 90 | k_EResultAccountLogonDeniedVerifiedEmailRequired = 74, 91 | k_EResultNoMatchingURL = 75, 92 | k_EResultBadResponse = 76, // parse failure, missing field, etc. 93 | k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password 94 | k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range 95 | k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen 96 | k_EResultDisabled = 80, // The requested service has been configured to be unavailable 97 | k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid ! 98 | k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action 99 | k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted 100 | k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent 101 | k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login 102 | k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted 103 | k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker 104 | k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch 105 | k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match 106 | k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners 107 | k_EResultNotModified = 91, // data not modified 108 | k_EResultNoMobileDevice = 92, // the account does not have a mobile device associated with it 109 | k_EResultTimeNotSynced = 93, // the time presented is out of range or tolerance 110 | k_EResultSmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.) 111 | k_EResultAccountLimitExceeded = 95, // Too many accounts access this resource 112 | k_EResultAccountActivityLimitExceeded = 96, // Too many changes to this account 113 | k_EResultPhoneActivityLimitExceeded = 97, // Too many changes to this phone 114 | k_EResultRefundToWallet = 98, // Cannot refund to payment method, must use wallet 115 | k_EResultEmailSendFailure = 99, // Cannot send an email 116 | k_EResultNotSettled = 100, // Can't perform operation till payment has settled 117 | k_EResultNeedCaptcha = 101, // Needs to provide a valid captcha 118 | k_EResultGSLTDenied = 102, // a game server login token owned by this token's owner has been banned 119 | k_EResultGSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone) 120 | k_EResultInvalidItemType = 104 // the type of thing we were requested to act on is invalid 121 | }; 122 | 123 | /* This enum is used in client API methods, do not re-number existing values. */ 124 | enum EHTTPMethod 125 | { 126 | k_EHTTPMethodInvalid = 0, 127 | k_EHTTPMethodGET, 128 | k_EHTTPMethodHEAD, 129 | k_EHTTPMethodPOST, 130 | k_EHTTPMethodPUT, 131 | k_EHTTPMethodDELETE, 132 | k_EHTTPMethodOPTIONS, 133 | 134 | // The remaining HTTP methods are not yet supported, per rfc2616 section 5.1.1 only GET and HEAD are required for 135 | // a compliant general purpose server. We'll likely add more as we find uses for them. 136 | 137 | // k_EHTTPMethodTRACE, 138 | // k_EHTTPMethodCONNECT 139 | }; 140 | 141 | 142 | /* HTTP Status codes that the server can send in response to a request, see rfc2616 section 10.3 for descriptions 143 | of each of these. */ 144 | enum EHTTPStatusCode 145 | { 146 | // Invalid status code (this isn't defined in HTTP, used to indicate unset in our code) 147 | k_EHTTPStatusCodeInvalid = 0, 148 | 149 | // Informational codes 150 | k_EHTTPStatusCode100Continue = 100, 151 | k_EHTTPStatusCode101SwitchingProtocols = 101, 152 | 153 | // Success codes 154 | k_EHTTPStatusCode200OK = 200, 155 | k_EHTTPStatusCode201Created = 201, 156 | k_EHTTPStatusCode202Accepted = 202, 157 | k_EHTTPStatusCode203NonAuthoritative = 203, 158 | k_EHTTPStatusCode204NoContent = 204, 159 | k_EHTTPStatusCode205ResetContent = 205, 160 | k_EHTTPStatusCode206PartialContent = 206, 161 | 162 | // Redirection codes 163 | k_EHTTPStatusCode300MultipleChoices = 300, 164 | k_EHTTPStatusCode301MovedPermanently = 301, 165 | k_EHTTPStatusCode302Found = 302, 166 | k_EHTTPStatusCode303SeeOther = 303, 167 | k_EHTTPStatusCode304NotModified = 304, 168 | k_EHTTPStatusCode305UseProxy = 305, 169 | //k_EHTTPStatusCode306Unused = 306, (used in old HTTP spec, now unused in 1.1) 170 | k_EHTTPStatusCode307TemporaryRedirect = 307, 171 | 172 | // Error codes 173 | k_EHTTPStatusCode400BadRequest = 400, 174 | k_EHTTPStatusCode401Unauthorized = 401, // You probably want 403 or something else. 401 implies you're sending a WWW-Authenticate header and the client can sent an Authorization header in response. 175 | k_EHTTPStatusCode402PaymentRequired = 402, // This is reserved for future HTTP specs, not really supported by clients 176 | k_EHTTPStatusCode403Forbidden = 403, 177 | k_EHTTPStatusCode404NotFound = 404, 178 | k_EHTTPStatusCode405MethodNotAllowed = 405, 179 | k_EHTTPStatusCode406NotAcceptable = 406, 180 | k_EHTTPStatusCode407ProxyAuthRequired = 407, 181 | k_EHTTPStatusCode408RequestTimeout = 408, 182 | k_EHTTPStatusCode409Conflict = 409, 183 | k_EHTTPStatusCode410Gone = 410, 184 | k_EHTTPStatusCode411LengthRequired = 411, 185 | k_EHTTPStatusCode412PreconditionFailed = 412, 186 | k_EHTTPStatusCode413RequestEntityTooLarge = 413, 187 | k_EHTTPStatusCode414RequestURITooLong = 414, 188 | k_EHTTPStatusCode415UnsupportedMediaType = 415, 189 | k_EHTTPStatusCode416RequestedRangeNotSatisfiable = 416, 190 | k_EHTTPStatusCode417ExpectationFailed = 417, 191 | k_EHTTPStatusCode4xxUnknown = 418, // 418 is reserved, so we'll use it to mean unknown 192 | k_EHTTPStatusCode429TooManyRequests = 429, 193 | 194 | // Server error codes 195 | k_EHTTPStatusCode500InternalServerError = 500, 196 | k_EHTTPStatusCode501NotImplemented = 501, 197 | k_EHTTPStatusCode502BadGateway = 502, 198 | k_EHTTPStatusCode503ServiceUnavailable = 503, 199 | k_EHTTPStatusCode504GatewayTimeout = 504, 200 | k_EHTTPStatusCode505HTTPVersionNotSupported = 505, 201 | k_EHTTPStatusCode5xxUnknown = 599, 202 | }; 203 | 204 | /* list of possible return values from the ISteamGameCoordinator API */ 205 | enum EGCResults 206 | { 207 | k_EGCResultOK = 0, 208 | k_EGCResultNoMessage = 1, // There is no message in the queue 209 | k_EGCResultBufferTooSmall = 2, // The buffer is too small for the requested message 210 | k_EGCResultNotLoggedOn = 3, // The client is not logged onto Steam 211 | k_EGCResultInvalidMessage = 4, // Something was wrong with the message being sent with SendMessage 212 | }; 213 | 214 | native bool SteamWorks_IsVACEnabled(); 215 | native bool SteamWorks_GetPublicIP(int ipaddr[4]); 216 | native int SteamWorks_GetPublicIPCell(); 217 | native bool SteamWorks_IsLoaded(); 218 | native bool SteamWorks_SetGameDescription(const char[] sDesc); 219 | native bool SteamWorks_IsConnected(); 220 | native bool SteamWorks_SetRule(const char[] sKey, const char[] sValue); 221 | native bool SteamWorks_ClearRules(); 222 | native bool SteamWorks_ForceHeartbeat(); 223 | native bool SteamWorks_GetUserGroupStatus(int client, int groupid); 224 | native bool SteamWorks_GetUserGroupStatusAuthID(int authid, int groupid); 225 | 226 | native EUserHasLicenseForAppResult SteamWorks_HasLicenseForApp(int client, int app); 227 | native EUserHasLicenseForAppResult SteamWorks_HasLicenseForAppId(int authid, int app); 228 | native SteamWorks_GetClientSteamID(int client, char[] sSteamID, int length); 229 | 230 | native bool SteamWorks_RequestStatsAuthID(int authid, int appid); 231 | native bool SteamWorks_RequestStats(int client, int appid); 232 | native bool SteamWorks_GetStatCell(int client, const char[] sKey, int &value); 233 | native bool SteamWorks_GetStatAuthIDCell(int authid, const char[] sKey, int &value); 234 | native bool SteamWorks_GetStatFloat(int client, const char[] sKey, float &value); 235 | native bool SteamWorks_GetStatAuthIDFloat(int authid, const char[] sKey, float &value); 236 | 237 | native Handle SteamWorks_CreateHTTPRequest(EHTTPMethod method, const char[] sURL); 238 | native bool SteamWorks_SetHTTPRequestContextValue(Handle hHandle, any data1, any data2=0); 239 | native bool SteamWorks_SetHTTPRequestNetworkActivityTimeout(Handle hHandle, int timeout); 240 | native bool SteamWorks_SetHTTPRequestHeaderValue(Handle hHandle, const char[] sName, const char[] sValue); 241 | native bool SteamWorks_SetHTTPRequestGetOrPostParameter(Handle hHandle, const char[] sName, const char[] sValue); 242 | 243 | typeset SteamWorksHTTPRequestCompleted 244 | { 245 | function void (Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode); 246 | function void (Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, any data1); 247 | function void (Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, any data1, any data2); 248 | }; 249 | 250 | typeset SteamWorksHTTPHeadersReceived 251 | { 252 | function void (Handle hRequest, bool bFailure); 253 | function void (Handle hRequest, bool bFailure, any data1); 254 | function void (Handle hRequest, bool bFailure, any data1, any data2); 255 | }; 256 | 257 | typeset SteamWorksHTTPDataReceived 258 | { 259 | function void (Handle hRequest, bool bFailure, int offset, int bytesreceived); 260 | function void (Handle hRequest, bool bFailure, int offset, int bytesreceived, any data1); 261 | function void (Handle hRequest, bool bFailure, int offset, int bytesreceived, any data1, any data2); 262 | }; 263 | 264 | native bool SteamWorks_SetHTTPCallbacks(Handle hHandle, SteamWorksHTTPRequestCompleted fCompleted = INVALID_FUNCTION, SteamWorksHTTPHeadersReceived fHeaders = INVALID_FUNCTION, SteamWorksHTTPDataReceived fData = INVALID_FUNCTION, Handle hCalling = INVALID_HANDLE); 265 | native bool SteamWorks_SendHTTPRequest(Handle hRequest); 266 | native bool SteamWorks_SendHTTPRequestAndStreamResponse(Handle hRequest); 267 | native bool SteamWorks_DeferHTTPRequest(Handle hRequest); 268 | native bool SteamWorks_PrioritizeHTTPRequest(Handle hRequest); 269 | native bool SteamWorks_GetHTTPResponseHeaderSize(Handle hRequest, const char[] sHeader, int &size); 270 | native bool SteamWorks_GetHTTPResponseHeaderValue(Handle hRequest, const char[] sHeader, char[] sValue, int size); 271 | native bool SteamWorks_GetHTTPResponseBodySize(Handle hRequest, int &size); 272 | native bool SteamWorks_GetHTTPResponseBodyData(Handle hRequest, char[] sBody, int length); 273 | native bool SteamWorks_GetHTTPStreamingResponseBodyData(Handle hRequest, int cOffset, char[] sBody, int length); 274 | native bool SteamWorks_GetHTTPDownloadProgressPct(Handle hRequest, float &percent); 275 | native bool SteamWorks_SetHTTPRequestRawPostBody(Handle hRequest, const char[] sContentType, const char[] sBody, int bodylen); 276 | 277 | typeset SteamWorksHTTPBodyCallback 278 | { 279 | function void (const char[] sData); 280 | function void (const char[] sData, any value); 281 | function void (const int[] data, any value, int datalen); 282 | }; 283 | 284 | native bool SteamWorks_GetHTTPResponseBodyCallback(Handle hRequest, SteamWorksHTTPBodyCallback fCallback, any data = 0, Handle hPlugin = INVALID_HANDLE); 285 | native bool SteamWorks_WriteHTTPResponseBodyToFile(Handle hRequest, const char[] sFileName); 286 | 287 | forward void SW_OnValidateClient(int ownerauthid, int authid); 288 | forward void SteamWorks_OnValidateClient(int ownerauthid, int authid); 289 | forward void SteamWorks_SteamServersConnected(); 290 | forward void SteamWorks_SteamServersConnectFailure(EResult result); 291 | forward void SteamWorks_SteamServersDisconnected(EResult result); 292 | 293 | forward Action SteamWorks_RestartRequested(); 294 | forward void SteamWorks_TokenRequested(char[] sToken, int maxlen); 295 | 296 | forward void SteamWorks_OnClientGroupStatus(int authid, int groupid, bool isMember, bool isOfficer); 297 | 298 | forward EGCResults SteamWorks_GCSendMessage(int unMsgType, const char[] pubData, int cubData); 299 | forward void SteamWorks_GCMsgAvailable(int cubData); 300 | forward EGCResults SteamWorks_GCRetrieveMessage(int punMsgType, const char[] pubDest, int cubDest, int pcubMsgSize); 301 | 302 | native EGCResults SteamWorks_SendMessageToGC(int unMsgType, const char[] pubData, int cubData); 303 | 304 | public Extension:__ext_SteamWorks = 305 | { 306 | name = "SteamWorks", 307 | file = "SteamWorks.ext", 308 | #if defined AUTOLOAD_EXTENSIONS 309 | autoload = 1, 310 | #else 311 | autoload = 0, 312 | #endif 313 | #if defined REQUIRE_EXTENSIONS 314 | required = 1, 315 | #else 316 | required = 0, 317 | #endif 318 | }; 319 | 320 | #if !defined REQUIRE_EXTENSIONS 321 | public __ext_SteamWorks_SetNTVOptional() 322 | { 323 | MarkNativeAsOptional("SteamWorks_IsVACEnabled"); 324 | MarkNativeAsOptional("SteamWorks_GetPublicIP"); 325 | MarkNativeAsOptional("SteamWorks_GetPublicIPCell"); 326 | MarkNativeAsOptional("SteamWorks_IsLoaded"); 327 | MarkNativeAsOptional("SteamWorks_SetGameDescription"); 328 | MarkNativeAsOptional("SteamWorks_IsConnected"); 329 | MarkNativeAsOptional("SteamWorks_SetRule"); 330 | MarkNativeAsOptional("SteamWorks_ClearRules"); 331 | MarkNativeAsOptional("SteamWorks_ForceHeartbeat"); 332 | MarkNativeAsOptional("SteamWorks_GetUserGroupStatus"); 333 | MarkNativeAsOptional("SteamWorks_GetUserGroupStatusAuthID"); 334 | 335 | MarkNativeAsOptional("SteamWorks_HasLicenseForApp"); 336 | MarkNativeAsOptional("SteamWorks_HasLicenseForAppId"); 337 | MarkNativeAsOptional("SteamWorks_GetClientSteamID"); 338 | 339 | MarkNativeAsOptional("SteamWorks_RequestStatsAuthID"); 340 | MarkNativeAsOptional("SteamWorks_RequestStats"); 341 | MarkNativeAsOptional("SteamWorks_GetStatCell"); 342 | MarkNativeAsOptional("SteamWorks_GetStatAuthIDCell"); 343 | MarkNativeAsOptional("SteamWorks_GetStatFloat"); 344 | MarkNativeAsOptional("SteamWorks_GetStatAuthIDFloat"); 345 | 346 | MarkNativeAsOptional("SteamWorks_SendMessageToGC"); 347 | 348 | MarkNativeAsOptional("SteamWorks_CreateHTTPRequest"); 349 | MarkNativeAsOptional("SteamWorks_SetHTTPRequestContextValue"); 350 | MarkNativeAsOptional("SteamWorks_SetHTTPRequestNetworkActivityTimeout"); 351 | MarkNativeAsOptional("SteamWorks_SetHTTPRequestHeaderValue"); 352 | MarkNativeAsOptional("SteamWorks_SetHTTPRequestGetOrPostParameter"); 353 | 354 | MarkNativeAsOptional("SteamWorks_SetHTTPCallbacks"); 355 | MarkNativeAsOptional("SteamWorks_SendHTTPRequest"); 356 | MarkNativeAsOptional("SteamWorks_SendHTTPRequestAndStreamResponse"); 357 | MarkNativeAsOptional("SteamWorks_DeferHTTPRequest"); 358 | MarkNativeAsOptional("SteamWorks_PrioritizeHTTPRequest"); 359 | MarkNativeAsOptional("SteamWorks_GetHTTPResponseHeaderSize"); 360 | MarkNativeAsOptional("SteamWorks_GetHTTPResponseHeaderValue"); 361 | MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodySize"); 362 | MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodyData"); 363 | MarkNativeAsOptional("SteamWorks_GetHTTPStreamingResponseBodyData"); 364 | MarkNativeAsOptional("SteamWorks_GetHTTPDownloadProgressPct"); 365 | MarkNativeAsOptional("SteamWorks_SetHTTPRequestRawPostBody"); 366 | 367 | MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodyCallback"); 368 | MarkNativeAsOptional("SteamWorks_WriteHTTPResponseBodyToFile"); 369 | } 370 | #endif 371 | -------------------------------------------------------------------------------- /scripting/include/colorvariables.inc: -------------------------------------------------------------------------------- 1 | // ===================================== 2 | // ===== ! ! ! W A R N I N G ! ! ! ===== 3 | // ===================================== 4 | // In case you want to update this file from the source below, 5 | // note that additional color names(aliases) were added for ckSurf. 6 | // Please don't forget to readd them after updating this file. 7 | // ===================================== /CK 8 | 9 | 10 | #if defined _colorvariables_included 11 | #endinput 12 | #endif 13 | #define _colorvariables_included "1.3" 14 | 15 | // Source: https://raw.githubusercontent.com/Drixevel/Chat-Processor/master/scripting/include/colorvariables.inc 16 | // Author: Raska aka KissLick 17 | // Syntax update: Keith Warren (Drixevel) (redwerewolf on Allied Mods) 18 | 19 | // ---------------------------------------------------------------------------------------- 20 | #define _CV_MAX_MESSAGE_LENGTH 1024 21 | #define _CV_MAX_VARIABLE_REDIRECTS 10 22 | #define _CV_CONFIG_DIRECTORY "configs/colorvariables" 23 | 24 | static bool g_bInit = false; 25 | static Handle g_hColors = INVALID_HANDLE; 26 | static char g_sConfigGlobal[PLATFORM_MAX_PATH]; 27 | static char g_sConfig[PLATFORM_MAX_PATH]; 28 | static char g_sChatPrefix[64] = ""; 29 | 30 | static bool g_bIgnorePrefix = false; 31 | static int g_iAuthor; 32 | static bool g_bSkipPlayers[MAXPLAYERS + 1] = {false, ...}; 33 | 34 | static Handle g_hForwardedVariable; 35 | 36 | enum triple { 37 | unknown = -1, 38 | yes = true, 39 | no = false 40 | }; 41 | static triple g_IsSource2009 = unknown; 42 | // ---------------------------------------------------------------------------------------- 43 | 44 | forward void COnForwardedVariable(char[] sCode, char[] sData, int iDataSize, char[] sColor, int iColorSize); 45 | 46 | stock void CSetPrefix(const char[] sPrefix, any ...) 47 | { 48 | VFormat(g_sChatPrefix, sizeof(g_sChatPrefix), sPrefix, 2); 49 | } 50 | 51 | stock void CSavePrefix(const char[] sPrefix, any ...) 52 | { 53 | char m_sPrefix[64]; 54 | VFormat(m_sPrefix, sizeof(m_sPrefix), sPrefix, 2); 55 | 56 | CAddVariable("&prefix", m_sPrefix, true); 57 | } 58 | 59 | stock void CSkipNextPrefix() 60 | { 61 | g_bIgnorePrefix = true; 62 | } 63 | 64 | stock void CSetNextAuthor(int iClient) 65 | { 66 | if (iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient)) { 67 | ThrowError("Invalid client index %i", iClient); 68 | } 69 | g_iAuthor = iClient; 70 | } 71 | 72 | stock void CSkipNextClient(int iClient) 73 | { 74 | if (iClient < 1 || iClient > MaxClients) { 75 | ThrowError("Invalid client index %i", iClient); 76 | } 77 | g_bSkipPlayers[iClient] = true; 78 | } 79 | 80 | /** 81 | * Prints a message to all clients in the chat area. 82 | * Supports color tags. 83 | * 84 | * @param iClient Client index. 85 | * @param sMessage Message (formatting rules) 86 | */ 87 | stock void CPrintToChat(int iClient, const char[] sMessage, any ...) 88 | { 89 | if (iClient < 1 || iClient > MaxClients) { 90 | ThrowError("Invalid client index %d", iClient); 91 | } 92 | 93 | if (!IsClientInGame(iClient)) { 94 | ThrowError("Client %d is not in game", iClient); 95 | } 96 | 97 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; 98 | SetGlobalTransTarget(iClient); 99 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 3); 100 | 101 | AddPrefixAndDefaultColor(sBuffer, sizeof(sBuffer)); 102 | g_bIgnorePrefix = false; 103 | 104 | CProcessVariables(sBuffer, sizeof(sBuffer)); 105 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 106 | 107 | SendPlayerMessage(iClient, sBuffer, g_iAuthor); 108 | g_iAuthor = 0; 109 | } 110 | 111 | stock void CPrintToChatAll(const char[] sMessage, any ...) 112 | { 113 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; 114 | 115 | for (int iClient = 1; iClient <= MaxClients; iClient++) { 116 | if (!IsClientInGame(iClient) || g_bSkipPlayers[iClient]) { 117 | g_bSkipPlayers[iClient] = false; 118 | continue; 119 | } 120 | 121 | SetGlobalTransTarget(iClient); 122 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 2); 123 | 124 | AddPrefixAndDefaultColor(sBuffer, sizeof(sBuffer)); 125 | g_bIgnorePrefix = false; 126 | 127 | CProcessVariables(sBuffer, sizeof(sBuffer)); 128 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 129 | 130 | SendPlayerMessage(iClient, sBuffer, g_iAuthor); 131 | } 132 | g_iAuthor = 0; 133 | } 134 | 135 | stock void CPrintToChatTeam(int iTeam, const char[] sMessage, any ...) 136 | { 137 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; 138 | 139 | for (int iClient = 1; iClient <= MaxClients; iClient++) { 140 | if (!IsClientInGame(iClient) || GetClientTeam(iClient) != iTeam || g_bSkipPlayers[iClient]) { 141 | g_bSkipPlayers[iClient] = false; 142 | continue; 143 | } 144 | 145 | SetGlobalTransTarget(iClient); 146 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 3); 147 | 148 | AddPrefixAndDefaultColor(sBuffer, sizeof(sBuffer)); 149 | g_bIgnorePrefix = false; 150 | 151 | CProcessVariables(sBuffer, sizeof(sBuffer)); 152 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 153 | 154 | SendPlayerMessage(iClient, sBuffer, g_iAuthor); 155 | } 156 | g_iAuthor = 0; 157 | } 158 | 159 | stock void CPrintToChatAdmins(int iBitFlags, const char[] sMessage, any ...) 160 | { 161 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; 162 | AdminId iAdminID; 163 | 164 | for (int iClient = 1; iClient <= MaxClients; iClient++) { 165 | if (!IsClientInGame(iClient) || g_bSkipPlayers[iClient]) { 166 | g_bSkipPlayers[iClient] = false; 167 | continue; 168 | } 169 | 170 | iAdminID = GetUserAdmin(iClient); 171 | if (iAdminID == INVALID_ADMIN_ID || !(GetAdminFlags(iAdminID, Access_Effective) & iBitFlags)) { 172 | continue; 173 | } 174 | 175 | SetGlobalTransTarget(iClient); 176 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 3); 177 | 178 | AddPrefixAndDefaultColor(sBuffer, sizeof(sBuffer)); 179 | g_bIgnorePrefix = false; 180 | 181 | CProcessVariables(sBuffer, sizeof(sBuffer)); 182 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 183 | 184 | SendPlayerMessage(iClient, sBuffer, g_iAuthor); 185 | } 186 | g_iAuthor = 0; 187 | } 188 | 189 | stock void CReplyToCommand(int iClient, const char[] sMessage, any ...) 190 | { 191 | if (iClient < 0 || iClient > MaxClients) { 192 | ThrowError("Invalid client index %d", iClient); 193 | } 194 | 195 | if (iClient != 0 && !IsClientInGame(iClient)) { 196 | ThrowError("Client %d is not in game", iClient); 197 | } 198 | 199 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; 200 | SetGlobalTransTarget(iClient); 201 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 3); 202 | 203 | AddPrefixAndDefaultColor(sBuffer, sizeof(sBuffer), "reply2cmd"); 204 | g_bIgnorePrefix = false; 205 | 206 | if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { 207 | CRemoveColors(sBuffer, sizeof(sBuffer)); 208 | PrintToConsole(iClient, "%s", sBuffer); 209 | } else { 210 | CPrintToChat(iClient, "%s", sBuffer); 211 | } 212 | } 213 | 214 | stock void CShowActivity(int iClient, const char[] sMessage, any ...) 215 | { 216 | if (iClient < 0 || iClient > MaxClients) { 217 | ThrowError("Invalid client index %d", iClient); 218 | } 219 | 220 | if (iClient != 0 && !IsClientInGame(iClient)) { 221 | ThrowError("Client %d is not in game", iClient); 222 | } 223 | 224 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; 225 | SetGlobalTransTarget(iClient); 226 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 3); 227 | Format(sBuffer, sizeof(sBuffer), "{showactivity}%s", sBuffer); 228 | CProcessVariables(sBuffer, sizeof(sBuffer)); 229 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 230 | 231 | ShowActivity(iClient, "%s", sBuffer); 232 | } 233 | 234 | stock void CShowActivityEx(int iClient, const char[] sTag, const char[] sMessage, any ...) 235 | { 236 | if (iClient < 0 || iClient > MaxClients) { 237 | ThrowError("Invalid client index %d", iClient); 238 | } 239 | 240 | if (iClient != 0 && !IsClientInGame(iClient)) { 241 | ThrowError("Client %d is not in game", iClient); 242 | } 243 | 244 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; char sBufferTag[_CV_MAX_MESSAGE_LENGTH]; 245 | SetGlobalTransTarget(iClient); 246 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 4); 247 | Format(sBuffer, sizeof(sBuffer), "{showactivity}%s", sBuffer); 248 | CProcessVariables(sBuffer, sizeof(sBuffer)); 249 | Format(sBufferTag, sizeof(sBufferTag), "{prefix}%s", sTag); 250 | CProcessVariables(sBufferTag, sizeof(sBufferTag)); 251 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 252 | CAddWhiteSpace(sBufferTag, sizeof(sBufferTag)); 253 | 254 | ShowActivityEx(iClient, sBufferTag, " %s", sBuffer); 255 | } 256 | 257 | stock void CShowActivity2(int iClient, const char[] sTag, const char[] sMessage, any ...) 258 | { 259 | if (iClient < 0 || iClient > MaxClients) { 260 | ThrowError("Invalid client index %d", iClient); 261 | } 262 | 263 | if (iClient != 0 && !IsClientInGame(iClient)) { 264 | ThrowError("Client %d is not in game", iClient); 265 | } 266 | 267 | char sBuffer[_CV_MAX_MESSAGE_LENGTH]; char sBufferTag[_CV_MAX_MESSAGE_LENGTH]; 268 | SetGlobalTransTarget(iClient); 269 | VFormat(sBuffer, sizeof(sBuffer), sMessage, 4); 270 | Format(sBuffer, sizeof(sBuffer), "{showactivity}%s", sBuffer); 271 | CProcessVariables(sBuffer, sizeof(sBuffer)); 272 | Format(sBufferTag, sizeof(sBufferTag), "{prefix}%s", sTag); 273 | CProcessVariables(sBufferTag, sizeof(sBufferTag)); 274 | CAddWhiteSpace(sBuffer, sizeof(sBuffer)); 275 | CAddWhiteSpace(sBufferTag, sizeof(sBufferTag)); 276 | 277 | ShowActivityEx(iClient, sBufferTag, " %s", sBuffer); 278 | } 279 | 280 | stock void CAddVariable(char[] sName, char[] sValue, bool bOnlySaveToConfig = false) 281 | { 282 | if (Init()) { 283 | if (!FileExists(g_sConfig)) { 284 | LogError("Cannot add color variable to '%s' - file doesn't exist!", g_sConfig); 285 | return; 286 | } 287 | 288 | Handle hKV = CreateKeyValues("colorvariables"); 289 | 290 | if (!FileToKeyValues(hKV, g_sConfig)) { 291 | CloseHandle(hKV); 292 | LogError("Cannot open file (for adding color variable) '%s' !", g_sConfig); 293 | return; 294 | } 295 | 296 | if (!KvJumpToKey(hKV, sName)) { 297 | StringToLower(sName); 298 | KvSetString(hKV, sName, sValue); 299 | 300 | if (!bOnlySaveToConfig) { 301 | Handle hRedirect = CreateArray(64); 302 | PushArrayString(hRedirect, sName); 303 | SetTrieString(g_hColors, sName, sValue); 304 | SolveRedirects(g_hColors, hRedirect); 305 | CloseHandle(hRedirect); 306 | } 307 | } 308 | 309 | KvRewind(hKV); 310 | KeyValuesToFile(hKV, g_sConfig); 311 | CloseHandle(hKV); 312 | } 313 | } 314 | 315 | stock void CLoadPluginConfig(const char[] sPluginName, bool bAllowPrefix = true) 316 | { 317 | if (Init()) { 318 | char sConfig[PLATFORM_MAX_PATH]; 319 | strcopy(sConfig, sizeof(sConfig), sPluginName); 320 | ReplaceStringEx(sConfig, sizeof(sConfig), ".smx", ""); 321 | BuildPath(Path_SM, sConfig, sizeof(sConfig), "%s/plugin.%s.cfg", _CV_CONFIG_DIRECTORY, sConfig); 322 | 323 | if (!FileExists(sConfig)) { 324 | LogError("Cannot load color variables from file '%s' - file doesn't exist!", sConfig); 325 | return; 326 | } 327 | 328 | Handle hRedirect = CreateArray(64); 329 | LoadConfigFile(g_hColors, sConfig, hRedirect, bAllowPrefix); 330 | SolveRedirects(g_hColors, hRedirect); 331 | CloseHandle(hRedirect); 332 | } 333 | } 334 | 335 | stock void CLoadPluginVariables(const char[] sPluginName, const char[][] sVariables, int iVariablesCount, bool bAllowPrefix = true) 336 | { 337 | if (Init() && iVariablesCount > 0) { 338 | char sConfig[PLATFORM_MAX_PATH]; 339 | strcopy(sConfig, sizeof(sConfig), sPluginName); 340 | ReplaceStringEx(sConfig, sizeof(sConfig), ".smx", ""); 341 | BuildPath(Path_SM, sConfig, sizeof(sConfig), "%s/plugin.%s.cfg", _CV_CONFIG_DIRECTORY, sConfig); 342 | 343 | if (!FileExists(sConfig)) { 344 | LogError("Cannot load color variables from file '%s' - file doesn't exist!", sConfig); 345 | return; 346 | } 347 | 348 | Handle hVariables = CreateTrie(); 349 | Handle hRedirect = CreateArray(64); 350 | LoadConfigFile(hVariables, sConfig, hRedirect, bAllowPrefix); 351 | SolveRedirects(hVariables, hRedirect); 352 | ClearArray(hRedirect); 353 | 354 | char sCode[64]; char sColor[64]; 355 | 356 | for (int i = 0; i < iVariablesCount; i++) { 357 | strcopy(sCode, sizeof(sCode), sVariables[i]); 358 | StringToLower(sCode); 359 | 360 | if (GetTrieString(hVariables, sCode, sColor, sizeof(sColor))) { 361 | SetTrieString(g_hColors, sCode, sColor); 362 | PushArrayString(hRedirect, sCode); 363 | } 364 | } 365 | 366 | SolveRedirects(g_hColors, hRedirect); 367 | CloseHandle(hRedirect); 368 | CloseHandle(hVariables); 369 | } 370 | } 371 | 372 | stock void CRemoveColors(char[] sMsg, int iSize) 373 | { 374 | CProcessVariables(sMsg, iSize, true); 375 | } 376 | 377 | stock void CProcessVariables(char[] sMsg, int iSize, bool bRemoveColors = false) 378 | { 379 | if (!Init()) { 380 | return; 381 | } 382 | 383 | char[] sOut = new char[iSize]; char[] sCode = new char[iSize]; char[] sColor = new char[iSize]; 384 | int iOutPos = 0; int iCodePos = -1; 385 | int iMsgLen = strlen(sMsg); 386 | 387 | for (int i = 0; i < iMsgLen; i++) { 388 | if (sMsg[i] == '{') { 389 | iCodePos = 0; 390 | } 391 | 392 | if (iCodePos > -1) { 393 | sCode[iCodePos] = sMsg[i]; 394 | sCode[iCodePos + 1] = '\0'; 395 | 396 | if (sMsg[i] == '}' || i == iMsgLen - 1) { 397 | strcopy(sCode, strlen(sCode) - 1, sCode[1]); 398 | StringToLower(sCode); 399 | 400 | if (CGetColor(sCode, sColor, iSize)) { 401 | if (!bRemoveColors) { 402 | StrCat(sOut, iSize, sColor); 403 | iOutPos += strlen(sColor); 404 | } 405 | } else { 406 | Format(sOut, iSize, "%s{%s}", sOut, sCode); 407 | iOutPos += strlen(sCode) + 2; 408 | } 409 | 410 | iCodePos = -1; 411 | strcopy(sCode, iSize, ""); 412 | strcopy(sColor, iSize, ""); 413 | } else { 414 | iCodePos++; 415 | } 416 | 417 | continue; 418 | } 419 | 420 | sOut[iOutPos] = sMsg[i]; 421 | iOutPos++; 422 | sOut[iOutPos] = '\0'; 423 | } 424 | 425 | strcopy(sMsg, iSize, sOut); 426 | } 427 | 428 | stock bool CGetColor(const char[] sName, char[] sColor, int iColorSize) 429 | { 430 | if (sName[0] == '\0') 431 | return false; 432 | 433 | if (sName[0] == '@') { 434 | int iSpace; 435 | char sData[64]; char m_sName[64]; 436 | strcopy(m_sName, sizeof(m_sName), sName[1]); 437 | 438 | if ((iSpace = FindCharInString(m_sName, ' ')) != -1 && (iSpace + 1 < strlen(m_sName))) { 439 | strcopy(m_sName, iSpace + 1, m_sName); 440 | strcopy(sData, sizeof(sData), m_sName[iSpace + 1]); 441 | } 442 | 443 | Call_StartForward(g_hForwardedVariable); 444 | Call_PushString(m_sName); 445 | Call_PushStringEx(sData, sizeof(sData), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, 0); 446 | Call_PushCell(sizeof(sData)); 447 | Call_PushStringEx(sColor, iColorSize, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); 448 | Call_PushCell(iColorSize); 449 | Call_Finish(); 450 | 451 | if (sColor[0] != '\0') { 452 | return true; 453 | } 454 | 455 | } else if (sName[0] == '#') { 456 | if (strlen(sName) == 7) { 457 | Format(sColor, iColorSize, "\x07%s", sName[1]); 458 | return true; 459 | } 460 | if (strlen(sName) == 9) { 461 | Format(sColor, iColorSize, "\x08%s", sName[1]); 462 | return true; 463 | } 464 | } else if (StrContains(sName, "player ", false) == 0 && strlen(sName) > 7) { 465 | int iClient = StringToInt(sName[7]); 466 | 467 | if (iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient)) { 468 | strcopy(sColor, iColorSize, "\x01"); 469 | LogError("Invalid client index %d", iClient); 470 | return false; 471 | } 472 | 473 | strcopy(sColor, iColorSize, "\x01"); 474 | switch (GetClientTeam(iClient)) { 475 | case 1: {GetTrieString(g_hColors, "team0", sColor, iColorSize);} 476 | case 2: {GetTrieString(g_hColors, "team1", sColor, iColorSize);} 477 | case 3: {GetTrieString(g_hColors, "team2", sColor, iColorSize);} 478 | } 479 | return true; 480 | } else { 481 | return GetTrieString(g_hColors, sName, sColor, iColorSize); 482 | } 483 | 484 | return false; 485 | } 486 | 487 | stock bool CExistColor(const char[] sName) 488 | { 489 | if (sName[0] == '\0' || sName[0] == '@' || sName[0] == '#') 490 | return false; 491 | 492 | char sColor[64]; 493 | return GetTrieString(g_hColors, sName, sColor, sizeof(sColor)); 494 | } 495 | 496 | stock void CSayText2(int iClient, char[] sMessage, int iAuthor, bool bChat = true) 497 | { 498 | Handle hMsg = StartMessageOne("SayText2", iClient, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); 499 | if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { 500 | PbSetInt(hMsg, "ent_idx", iAuthor); 501 | PbSetBool(hMsg, "chat", bChat); 502 | PbSetString(hMsg, "msg_name", sMessage); 503 | PbAddString(hMsg, "params", ""); 504 | PbAddString(hMsg, "params", ""); 505 | PbAddString(hMsg, "params", ""); 506 | PbAddString(hMsg, "params", ""); 507 | } else { 508 | BfWriteByte(hMsg, iAuthor); 509 | BfWriteByte(hMsg, true); 510 | BfWriteString(hMsg, sMessage); 511 | } 512 | EndMessage(); 513 | } 514 | 515 | stock void CAddWhiteSpace(char[] sBuffer, int iSize) 516 | { 517 | if (!IsSource2009()) { 518 | Format(sBuffer, iSize, " %s", sBuffer); 519 | } 520 | } 521 | 522 | // ---------------------------------------------------------------------------------------- 523 | // Private stuff 524 | // ---------------------------------------------------------------------------------------- 525 | 526 | stock bool Init() 527 | { 528 | if (g_bInit) { 529 | LoadColors(); 530 | return true; 531 | } 532 | 533 | char sPluginName[PLATFORM_MAX_PATH]; 534 | char sDirectoryPath[PLATFORM_MAX_PATH]; 535 | Handle hConfig = INVALID_HANDLE; 536 | GetPluginFilename(INVALID_HANDLE, sPluginName, sizeof(sPluginName)); 537 | ReplaceStringEx(sPluginName, sizeof(sPluginName), "\\", "/"); 538 | int iSlash = FindCharInString(sPluginName, '/', true); 539 | if (iSlash > -1) { 540 | strcopy(sPluginName, sizeof(sPluginName), sPluginName[iSlash + 1]); 541 | } 542 | ReplaceStringEx(sPluginName, sizeof(sPluginName), ".smx", ""); 543 | 544 | BuildPath(Path_SM, sDirectoryPath, sizeof(sDirectoryPath), "%s/", _CV_CONFIG_DIRECTORY); 545 | if (!DirExists(sDirectoryPath)) 546 | CreateDirectory(sDirectoryPath, 511); 547 | 548 | char sGlobalVariableList[15][2][64] = { 549 | {"prefix", "{engine 2}"}, 550 | {"default", "{engine 1}"}, 551 | {"reply2cmd", "{engine 1}"}, 552 | {"showactivity", "{engine 1}"}, 553 | {"", ""}, 554 | {"error", "{engine 3}"}, 555 | {"", ""}, 556 | {"highlight", "{engine 2}"}, 557 | {"player", "{engine 2}"}, 558 | {"settings", "{engine 2}"}, 559 | {"command", "{engine 2}"}, 560 | {"", ""}, 561 | {"team0", "{engine 8}"}, 562 | {"team1", "{engine 9}"}, 563 | {"team2", "{engine 11}"} 564 | }; 565 | 566 | if (IsSource2009()) { 567 | strcopy(sGlobalVariableList[12][1], 64, "{#cccccc}"); 568 | strcopy(sGlobalVariableList[13][1], 64, "{#ff4040}"); 569 | strcopy(sGlobalVariableList[14][1], 64, "{#4d7942}"); 570 | } 571 | 572 | BuildPath(Path_SM, g_sConfigGlobal, sizeof(g_sConfigGlobal), "%s/global.cfg", _CV_CONFIG_DIRECTORY); 573 | if (!FileExists(g_sConfigGlobal)) { 574 | hConfig = OpenFile(g_sConfigGlobal, "w"); 575 | if (hConfig == INVALID_HANDLE) { 576 | LogError("Cannot create file '%s' !", g_sConfigGlobal); 577 | return false; 578 | } 579 | 580 | WriteFileLine(hConfig, "// Version: %s", _colorvariables_included); 581 | WriteFileLine(hConfig, "\"colorvariables\""); 582 | WriteFileLine(hConfig, "{"); 583 | for (int i = 0; i < 15; i++) { 584 | if (sGlobalVariableList[i][0][0] == '\0') { 585 | WriteFileLine(hConfig, ""); 586 | } else { 587 | WriteFileLine(hConfig, "\t\"%s\" \"%s\"", sGlobalVariableList[i][0], sGlobalVariableList[i][1]); 588 | } 589 | } 590 | WriteFileLine(hConfig, "}"); 591 | 592 | CloseHandle(hConfig); 593 | hConfig = INVALID_HANDLE; 594 | } else { 595 | hConfig = OpenFile(g_sConfigGlobal, "r"); 596 | if (hConfig == INVALID_HANDLE) { 597 | LogError("Cannot read from file '%s' !", g_sConfigGlobal); 598 | return false; 599 | } 600 | 601 | char sVersionLine[64]; 602 | ReadFileLine(hConfig, sVersionLine, sizeof(sVersionLine)); 603 | CloseHandle(hConfig); 604 | 605 | TrimString(sVersionLine); 606 | strcopy(sVersionLine, sizeof(sVersionLine), sVersionLine[FindCharInString(sVersionLine, ':') + 2]); 607 | 608 | if (StringToFloat(sVersionLine) < StringToFloat(_colorvariables_included)) { 609 | Handle hKV = CreateKeyValues("colorvariables"); 610 | 611 | if (!FileToKeyValues(hKV, g_sConfigGlobal) || !KvGotoFirstSubKey(hKV, false)) { 612 | CloseHandle(hKV); 613 | LogError("Cannot read variables from file '%s' !", g_sConfigGlobal); 614 | return false; 615 | } 616 | 617 | for (int i = 0; i < 15; i++) { 618 | if (sGlobalVariableList[i][0][0] == '\0') 619 | continue; 620 | 621 | if (!KvJumpToKey(hKV, sGlobalVariableList[i][0])) 622 | KvSetString(hKV, sGlobalVariableList[i][0], sGlobalVariableList[i][1]); 623 | } 624 | 625 | hConfig = OpenFile(g_sConfigGlobal, "w"); 626 | if (hConfig == INVALID_HANDLE) { 627 | LogError("Cannot write to file '%s' !", g_sConfigGlobal); 628 | return false; 629 | } 630 | 631 | WriteFileLine(hConfig, "// Version: %s", _colorvariables_included); 632 | WriteFileLine(hConfig, "\"colorvariables\""); 633 | WriteFileLine(hConfig, "{"); 634 | 635 | char sCode[64]; char sColor[64]; 636 | 637 | KvGotoFirstSubKey(hKV, false); 638 | do 639 | { 640 | KvGetSectionName(hKV, sCode, sizeof(sCode)); 641 | KvGetString(hKV, NULL_STRING, sColor, sizeof(sColor)); 642 | StringToLower(sCode); 643 | StringToLower(sColor); 644 | 645 | WriteFileLine(hConfig, "\t\"%s\" \"%s\"", sCode, sColor); 646 | } while (KvGotoNextKey(hKV, false)); 647 | 648 | WriteFileLine(hConfig, "}"); 649 | 650 | CloseHandle(hConfig); 651 | CloseHandle(hKV); 652 | } 653 | } 654 | 655 | BuildPath(Path_SM, g_sConfig, sizeof(g_sConfig), "%s/plugin.%s.cfg", _CV_CONFIG_DIRECTORY, sPluginName); 656 | if (!FileExists(g_sConfig)) { 657 | hConfig = OpenFile(g_sConfig, "w"); 658 | if (hConfig == INVALID_HANDLE) { 659 | LogError("Cannot create file '%s' !", g_sConfig); 660 | return false; 661 | } 662 | 663 | WriteFileLine(hConfig, "\"colorvariables\"\n{\n}"); 664 | CloseHandle(hConfig); 665 | hConfig = INVALID_HANDLE; 666 | } 667 | 668 | for (int iClient = 1; iClient <= MaxClients; iClient++) { 669 | g_bSkipPlayers[iClient] = false; 670 | } 671 | 672 | g_hForwardedVariable = CreateGlobalForward("COnForwardedVariable", ET_Ignore, Param_String, Param_String, Param_Cell, Param_String, Param_Cell); 673 | 674 | LoadColors(); 675 | g_bInit = true; 676 | return true; 677 | } 678 | 679 | stock void LoadColors() 680 | { 681 | if (g_hColors == INVALID_HANDLE) { 682 | g_hColors = CreateTrie(); 683 | Handle hRedirect = CreateArray(64); 684 | 685 | AddColors(g_hColors); 686 | LoadConfigFile(g_hColors, g_sConfigGlobal, hRedirect); 687 | LoadConfigFile(g_hColors, g_sConfig, hRedirect); 688 | 689 | SolveRedirects(g_hColors, hRedirect); 690 | CloseHandle(hRedirect); 691 | } 692 | } 693 | 694 | stock void LoadConfigFile(Handle hTrie, char[] sPath, Handle hRedirect, bool bAllowPrefix = true) 695 | { 696 | if (!FileExists(sPath)) { 697 | LogError("Cannot load color variables from file '%s' - file doesn't exist!", sPath); 698 | return; 699 | } 700 | 701 | Handle hKV = CreateKeyValues("colorvariables"); 702 | 703 | if (!FileToKeyValues(hKV, sPath)) { 704 | CloseHandle(hKV); 705 | LogError("Cannot load color variables from file '%s' !", sPath); 706 | return; 707 | } 708 | 709 | if (!KvGotoFirstSubKey(hKV, false)) { 710 | CloseHandle(hKV); 711 | return; 712 | } 713 | 714 | char sCode[64]; char sColor[64]; 715 | 716 | do 717 | { 718 | KvGetSectionName(hKV, sCode, sizeof(sCode)); 719 | KvGetString(hKV, NULL_STRING, sColor, sizeof(sColor)); 720 | 721 | if (bAllowPrefix && StrEqual(sCode, "&prefix", false)) { 722 | CSetPrefix(sColor); 723 | continue; 724 | } 725 | 726 | StringToLower(sCode); 727 | 728 | if (HasBrackets(sColor) && sColor[1] == '@') { 729 | LogError("Variables cannot be redirected to forwarded variables! (variable '%s')", sCode); 730 | continue; 731 | } 732 | 733 | if (HasBrackets(sColor)) { 734 | if (sColor[1] == '#') { 735 | Format(sColor, sizeof(sColor), "\x07%s", sColor[1]); 736 | } else { 737 | PushArrayString(hRedirect, sCode); 738 | } 739 | } 740 | 741 | SetTrieString(hTrie, sCode, sColor); 742 | } while (KvGotoNextKey(hKV, false)); 743 | 744 | CloseHandle(hKV); 745 | } 746 | 747 | stock void SolveRedirects(Handle hTrie, Handle hRedirect) 748 | { 749 | char sCode[64]; char sRedirect[64]; char sColor[64]; char sFirstColor[64]; 750 | int iRedirectLife; bool bHasBrackets; 751 | 752 | for (int i = 0; i < GetArraySize(hRedirect); i++) { 753 | GetArrayString(hRedirect, i, sRedirect, sizeof(sRedirect)); 754 | strcopy(sCode, sizeof(sCode), sRedirect); 755 | bHasBrackets = true; 756 | 757 | GetTrieString(hTrie, sRedirect, sColor, sizeof(sColor)); 758 | strcopy(sFirstColor, sizeof(sFirstColor), sRedirect); 759 | iRedirectLife = _CV_MAX_VARIABLE_REDIRECTS; 760 | 761 | do { 762 | if (!HasBrackets(sColor)) { 763 | strcopy(sRedirect, sizeof(sRedirect), sColor); 764 | bHasBrackets = false; 765 | break; 766 | } 767 | 768 | strcopy(sColor, strlen(sColor) - 1, sColor[1]); 769 | if (iRedirectLife > 0) { 770 | strcopy(sRedirect, sizeof(sRedirect), sColor); 771 | iRedirectLife--; 772 | } else { 773 | strcopy(sRedirect, sizeof(sRedirect), sFirstColor); 774 | LogError("Too many redirects for variable '%s' !", sCode); 775 | break; 776 | } 777 | } while (GetTrieString(hTrie, sRedirect, sColor, sizeof(sColor))); 778 | 779 | if (bHasBrackets) { 780 | Format(sRedirect, sizeof(sRedirect), "{%s}", sRedirect); 781 | } 782 | 783 | StringToLower(sCode); 784 | StringToLower(sRedirect); 785 | SetTrieString(hTrie, sCode, sRedirect); 786 | } 787 | } 788 | 789 | stock bool HasBrackets(const char[] sSource) 790 | { 791 | return (sSource[0] == '{' && sSource[strlen(sSource) - 1] == '}'); 792 | } 793 | 794 | stock void StringToLower(char[] sSource) 795 | { 796 | for (int i = 0; i < strlen(sSource); i++) { 797 | if (sSource[i] == '\0') 798 | break; 799 | 800 | sSource[i] = CharToLower(sSource[i]); 801 | } 802 | } 803 | 804 | stock void AddColors(Handle hTrie) 805 | { 806 | if (IsSource2009()) { 807 | SetTrieString(hTrie, "default", "\x01"); 808 | SetTrieString(hTrie, "teamcolor", "\x03"); 809 | 810 | SetTrieString(hTrie, "aliceblue", "\x07F0F8FF"); 811 | SetTrieString(hTrie, "allies", "\x074D7942"); 812 | SetTrieString(hTrie, "ancient", "\x07EB4B4B"); 813 | SetTrieString(hTrie, "antiquewhite", "\x07FAEBD7"); 814 | SetTrieString(hTrie, "aqua", "\x0700FFFF"); 815 | SetTrieString(hTrie, "aquamarine", "\x077FFFD4"); 816 | SetTrieString(hTrie, "arcana", "\x07ADE55C"); 817 | SetTrieString(hTrie, "axis", "\x07FF4040"); 818 | SetTrieString(hTrie, "azure", "\x07007FFF"); 819 | SetTrieString(hTrie, "beige", "\x07F5F5DC"); 820 | SetTrieString(hTrie, "bisque", "\x07FFE4C4"); 821 | SetTrieString(hTrie, "black", "\x07000000"); 822 | SetTrieString(hTrie, "blanchedalmond", "\x07FFEBCD"); 823 | SetTrieString(hTrie, "blue", "\x0799CCFF"); 824 | SetTrieString(hTrie, "blueviolet", "\x078A2BE2"); 825 | SetTrieString(hTrie, "brown", "\x07A52A2A"); 826 | SetTrieString(hTrie, "burlywood", "\x07DEB887"); 827 | SetTrieString(hTrie, "cadetblue", "\x075F9EA0"); 828 | SetTrieString(hTrie, "chartreuse", "\x077FFF00"); 829 | SetTrieString(hTrie, "chocolate", "\x07D2691E"); 830 | SetTrieString(hTrie, "collectors", "\x07AA0000"); 831 | SetTrieString(hTrie, "common", "\x07B0C3D9"); 832 | SetTrieString(hTrie, "community", "\x0770B04A"); 833 | SetTrieString(hTrie, "coral", "\x07FF7F50"); 834 | SetTrieString(hTrie, "cornflowerblue", "\x076495ED"); 835 | SetTrieString(hTrie, "cornsilk", "\x07FFF8DC"); 836 | SetTrieString(hTrie, "corrupted", "\x07A32C2E"); 837 | SetTrieString(hTrie, "crimson", "\x07DC143C"); 838 | SetTrieString(hTrie, "cyan", "\x0700FFFF"); 839 | SetTrieString(hTrie, "darkblue", "\x0700008B"); 840 | SetTrieString(hTrie, "darkcyan", "\x07008B8B"); 841 | SetTrieString(hTrie, "darkgoldenrod", "\x07B8860B"); 842 | SetTrieString(hTrie, "darkgray", "\x07A9A9A9"); 843 | SetTrieString(hTrie, "darkgrey", "\x07A9A9A9"); 844 | SetTrieString(hTrie, "darkgreen", "\x07006400"); 845 | SetTrieString(hTrie, "darkkhaki", "\x07BDB76B"); 846 | SetTrieString(hTrie, "darkmagenta", "\x078B008B"); 847 | SetTrieString(hTrie, "darkolivegreen", "\x07556B2F"); 848 | SetTrieString(hTrie, "darkorange", "\x07FF8C00"); 849 | SetTrieString(hTrie, "darkorchid", "\x079932CC"); 850 | SetTrieString(hTrie, "darkred", "\x078B0000"); 851 | SetTrieString(hTrie, "darksalmon", "\x07E9967A"); 852 | SetTrieString(hTrie, "darkseagreen", "\x078FBC8F"); 853 | SetTrieString(hTrie, "darkslateblue", "\x07483D8B"); 854 | SetTrieString(hTrie, "darkslategray", "\x072F4F4F"); 855 | SetTrieString(hTrie, "darkslategrey", "\x072F4F4F"); 856 | SetTrieString(hTrie, "darkturquoise", "\x0700CED1"); 857 | SetTrieString(hTrie, "darkviolet", "\x079400D3"); 858 | SetTrieString(hTrie, "deeppink", "\x07FF1493"); 859 | SetTrieString(hTrie, "deepskyblue", "\x0700BFFF"); 860 | SetTrieString(hTrie, "dimgray", "\x07696969"); 861 | SetTrieString(hTrie, "dimgrey", "\x07696969"); 862 | SetTrieString(hTrie, "dodgerblue", "\x071E90FF"); 863 | SetTrieString(hTrie, "exalted", "\x07CCCCCD"); 864 | SetTrieString(hTrie, "firebrick", "\x07B22222"); 865 | SetTrieString(hTrie, "floralwhite", "\x07FFFAF0"); 866 | SetTrieString(hTrie, "forestgreen", "\x07228B22"); 867 | SetTrieString(hTrie, "frozen", "\x074983B3"); 868 | SetTrieString(hTrie, "fuchsia", "\x07FF00FF"); 869 | SetTrieString(hTrie, "fullblue", "\x070000FF"); 870 | SetTrieString(hTrie, "fullred", "\x07FF0000"); 871 | SetTrieString(hTrie, "gainsboro", "\x07DCDCDC"); 872 | SetTrieString(hTrie, "genuine", "\x074D7455"); 873 | SetTrieString(hTrie, "ghostwhite", "\x07F8F8FF"); 874 | SetTrieString(hTrie, "gold", "\x07FFD700"); 875 | SetTrieString(hTrie, "goldenrod", "\x07DAA520"); 876 | SetTrieString(hTrie, "gray", "\x07CCCCCC"); 877 | SetTrieString(hTrie, "grey", "\x07CCCCCC"); 878 | SetTrieString(hTrie, "green", "\x073EFF3E"); 879 | SetTrieString(hTrie, "greenyellow", "\x07ADFF2F"); 880 | SetTrieString(hTrie, "haunted", "\x0738F3AB"); 881 | SetTrieString(hTrie, "honeydew", "\x07F0FFF0"); 882 | SetTrieString(hTrie, "hotpink", "\x07FF69B4"); 883 | SetTrieString(hTrie, "immortal", "\x07E4AE33"); 884 | SetTrieString(hTrie, "indianred", "\x07CD5C5C"); 885 | SetTrieString(hTrie, "indigo", "\x074B0082"); 886 | SetTrieString(hTrie, "ivory", "\x07FFFFF0"); 887 | SetTrieString(hTrie, "khaki", "\x07F0E68C"); 888 | SetTrieString(hTrie, "lavender", "\x07E6E6FA"); 889 | SetTrieString(hTrie, "lavenderblush", "\x07FFF0F5"); 890 | SetTrieString(hTrie, "lawngreen", "\x077CFC00"); 891 | SetTrieString(hTrie, "legendary", "\x07D32CE6"); 892 | SetTrieString(hTrie, "lemonchiffon", "\x07FFFACD"); 893 | SetTrieString(hTrie, "lightblue", "\x07ADD8E6"); 894 | SetTrieString(hTrie, "lightcoral", "\x07F08080"); 895 | SetTrieString(hTrie, "lightcyan", "\x07E0FFFF"); 896 | SetTrieString(hTrie, "lightgoldenrodyellow", "\x07FAFAD2"); 897 | SetTrieString(hTrie, "lightgray", "\x07D3D3D3"); 898 | SetTrieString(hTrie, "lightgrey", "\x07D3D3D3"); 899 | SetTrieString(hTrie, "lightgreen", "\x0799FF99"); 900 | SetTrieString(hTrie, "lightpink", "\x07FFB6C1"); 901 | SetTrieString(hTrie, "lightsalmon", "\x07FFA07A"); 902 | SetTrieString(hTrie, "lightseagreen", "\x0720B2AA"); 903 | SetTrieString(hTrie, "lightskyblue", "\x0787CEFA"); 904 | SetTrieString(hTrie, "lightslategray", "\x07778899"); 905 | SetTrieString(hTrie, "lightslategrey", "\x07778899"); 906 | SetTrieString(hTrie, "lightsteelblue", "\x07B0C4DE"); 907 | SetTrieString(hTrie, "lightyellow", "\x07FFFFE0"); 908 | SetTrieString(hTrie, "lime", "\x0700FF00"); 909 | SetTrieString(hTrie, "limegreen", "\x0732CD32"); 910 | SetTrieString(hTrie, "linen", "\x07FAF0E6"); 911 | SetTrieString(hTrie, "magenta", "\x07FF00FF"); 912 | SetTrieString(hTrie, "maroon", "\x07800000"); 913 | SetTrieString(hTrie, "mediumaquamarine", "\x0766CDAA"); 914 | SetTrieString(hTrie, "mediumblue", "\x070000CD"); 915 | SetTrieString(hTrie, "mediumorchid", "\x07BA55D3"); 916 | SetTrieString(hTrie, "mediumpurple", "\x079370D8"); 917 | SetTrieString(hTrie, "mediumseagreen", "\x073CB371"); 918 | SetTrieString(hTrie, "mediumslateblue", "\x077B68EE"); 919 | SetTrieString(hTrie, "mediumspringgreen", "\x0700FA9A"); 920 | SetTrieString(hTrie, "mediumturquoise", "\x0748D1CC"); 921 | SetTrieString(hTrie, "mediumvioletred", "\x07C71585"); 922 | SetTrieString(hTrie, "midnightblue", "\x07191970"); 923 | SetTrieString(hTrie, "mintcream", "\x07F5FFFA"); 924 | SetTrieString(hTrie, "mistyrose", "\x07FFE4E1"); 925 | SetTrieString(hTrie, "moccasin", "\x07FFE4B5"); 926 | SetTrieString(hTrie, "mythical", "\x078847FF"); 927 | SetTrieString(hTrie, "navajowhite", "\x07FFDEAD"); 928 | SetTrieString(hTrie, "navy", "\x07000080"); 929 | SetTrieString(hTrie, "normal", "\x07B2B2B2"); 930 | SetTrieString(hTrie, "oldlace", "\x07FDF5E6"); 931 | SetTrieString(hTrie, "olive", "\x079EC34F"); 932 | SetTrieString(hTrie, "olivedrab", "\x076B8E23"); 933 | SetTrieString(hTrie, "orange", "\x07FFA500"); 934 | SetTrieString(hTrie, "orangered", "\x07FF4500"); 935 | SetTrieString(hTrie, "orchid", "\x07DA70D6"); 936 | SetTrieString(hTrie, "palegoldenrod", "\x07EEE8AA"); 937 | SetTrieString(hTrie, "palegreen", "\x0798FB98"); 938 | SetTrieString(hTrie, "paleturquoise", "\x07AFEEEE"); 939 | SetTrieString(hTrie, "palevioletred", "\x07D87093"); 940 | SetTrieString(hTrie, "papayawhip", "\x07FFEFD5"); 941 | SetTrieString(hTrie, "peachpuff", "\x07FFDAB9"); 942 | SetTrieString(hTrie, "peru", "\x07CD853F"); 943 | SetTrieString(hTrie, "pink", "\x07FFC0CB"); 944 | SetTrieString(hTrie, "plum", "\x07DDA0DD"); 945 | SetTrieString(hTrie, "powderblue", "\x07B0E0E6"); 946 | SetTrieString(hTrie, "purple", "\x07800080"); 947 | SetTrieString(hTrie, "rare", "\x074B69FF"); 948 | SetTrieString(hTrie, "red", "\x07FF4040"); 949 | SetTrieString(hTrie, "rosybrown", "\x07BC8F8F"); 950 | SetTrieString(hTrie, "royalblue", "\x074169E1"); 951 | SetTrieString(hTrie, "saddlebrown", "\x078B4513"); 952 | SetTrieString(hTrie, "salmon", "\x07FA8072"); 953 | SetTrieString(hTrie, "sandybrown", "\x07F4A460"); 954 | SetTrieString(hTrie, "seagreen", "\x072E8B57"); 955 | SetTrieString(hTrie, "seashell", "\x07FFF5EE"); 956 | SetTrieString(hTrie, "selfmade", "\x0770B04A"); 957 | SetTrieString(hTrie, "sienna", "\x07A0522D"); 958 | SetTrieString(hTrie, "silver", "\x07C0C0C0"); 959 | SetTrieString(hTrie, "skyblue", "\x0787CEEB"); 960 | SetTrieString(hTrie, "slateblue", "\x076A5ACD"); 961 | SetTrieString(hTrie, "slategray", "\x07708090"); 962 | SetTrieString(hTrie, "slategrey", "\x07708090"); 963 | SetTrieString(hTrie, "snow", "\x07FFFAFA"); 964 | SetTrieString(hTrie, "springgreen", "\x0700FF7F"); 965 | SetTrieString(hTrie, "steelblue", "\x074682B4"); 966 | SetTrieString(hTrie, "strange", "\x07CF6A32"); 967 | SetTrieString(hTrie, "tan", "\x07D2B48C"); 968 | SetTrieString(hTrie, "teal", "\x07008080"); 969 | SetTrieString(hTrie, "thistle", "\x07D8BFD8"); 970 | SetTrieString(hTrie, "tomato", "\x07FF6347"); 971 | SetTrieString(hTrie, "turquoise", "\x0740E0D0"); 972 | SetTrieString(hTrie, "uncommon", "\x07B0C3D9"); 973 | SetTrieString(hTrie, "unique", "\x07FFD700"); 974 | SetTrieString(hTrie, "unusual", "\x078650AC"); 975 | SetTrieString(hTrie, "valve", "\x07A50F79"); 976 | SetTrieString(hTrie, "vintage", "\x07476291"); 977 | SetTrieString(hTrie, "violet", "\x07EE82EE"); 978 | SetTrieString(hTrie, "wheat", "\x07F5DEB3"); 979 | SetTrieString(hTrie, "white", "\x07FFFFFF"); 980 | SetTrieString(hTrie, "whitesmoke", "\x07F5F5F5"); 981 | SetTrieString(hTrie, "yellow", "\x07FFFF00"); 982 | SetTrieString(hTrie, "yellowgreen", "\x079ACD32"); 983 | } else { 984 | SetTrieString(hTrie, "default", "\x01"); // "\x01" "{default}" 985 | SetTrieString(hTrie, "teamcolor", "\x03"); // "\x03" "{lightgreen}" "\x03" "{orange}" "\x03" "{blue}" "\x03" "{purple}" 986 | 987 | SetTrieString(hTrie, "red", "\x07"); // "\x07" "{red}" 988 | SetTrieString(hTrie, "lightred", "\x0F"); // "\x0F" "{lightred}" 989 | SetTrieString(hTrie, "darkred", "\x02"); // "\x02" "{darkred}" 990 | SetTrieString(hTrie, "bluegrey", "\x0A"); // "\x0A" "{lightblue}" 991 | SetTrieString(hTrie, "blue", "\x0B"); // "\x0B" "{steelblue}" 992 | SetTrieString(hTrie, "darkblue", "\x0C"); // "\x0C" "{darkblue}" 993 | SetTrieString(hTrie, "purple", "\x03"); 994 | SetTrieString(hTrie, "orchid", "\x0E"); // "\x0E" "{pink}" 995 | SetTrieString(hTrie, "yellow", "\x09"); // "\x09" "{yellow}" 996 | SetTrieString(hTrie, "gold", "\x10"); 997 | SetTrieString(hTrie, "lightgreen", "\x05"); // "\x05" "{olive}" 998 | SetTrieString(hTrie, "green", "\x04"); // "\x04" "{green}" 999 | SetTrieString(hTrie, "lime", "\x06"); // "\x06" "{lime}" 1000 | SetTrieString(hTrie, "grey", "\x08"); // "\x08" "{grey}" 1001 | SetTrieString(hTrie, "grey2", "\x0D"); 1002 | 1003 | // Additional color names for ckSurf backwards compatibility 1004 | SetTrieString(hTrie, "bluegray", "\x0A"); // using bluegrey 1005 | SetTrieString(hTrie, "gray", "\x08"); // using gray 1006 | SetTrieString(hTrie, "gray2", "\x0D"); // using gray2 1007 | SetTrieString(hTrie, "orange", "\x10"); // using gold 1008 | SetTrieString(hTrie, "steelblue", "\x0D"); // using grey2 1009 | SetTrieString(hTrie, "pink", "\x0E"); // using orchid 1010 | SetTrieString(hTrie, "lightblue", "\x0A"); // using bluegrey 1011 | SetTrieString(hTrie, "olive", "\x05"); // using lightgreen 1012 | } 1013 | 1014 | SetTrieString(hTrie, "engine 1", "\x01"); 1015 | SetTrieString(hTrie, "engine 2", "\x02"); 1016 | SetTrieString(hTrie, "engine 3", "\x03"); 1017 | SetTrieString(hTrie, "engine 4", "\x04"); 1018 | SetTrieString(hTrie, "engine 5", "\x05"); 1019 | SetTrieString(hTrie, "engine 6", "\x06"); 1020 | SetTrieString(hTrie, "engine 7", "\x07"); 1021 | SetTrieString(hTrie, "engine 8", "\x08"); 1022 | SetTrieString(hTrie, "engine 9", "\x09"); 1023 | SetTrieString(hTrie, "engine 10", "\x0A"); 1024 | SetTrieString(hTrie, "engine 11", "\x0B"); 1025 | SetTrieString(hTrie, "engine 12", "\x0C"); 1026 | SetTrieString(hTrie, "engine 13", "\x0D"); 1027 | SetTrieString(hTrie, "engine 14", "\x0E"); 1028 | SetTrieString(hTrie, "engine 15", "\x0F"); 1029 | SetTrieString(hTrie, "engine 16", "\x10"); 1030 | } 1031 | 1032 | stock bool IsSource2009() 1033 | { 1034 | if (g_IsSource2009 == unknown) { 1035 | EngineVersion iEngineVersion = GetEngineVersion(); 1036 | g_IsSource2009 = (iEngineVersion == Engine_CSS || iEngineVersion == Engine_TF2 || iEngineVersion == Engine_HL2DM || iEngineVersion == Engine_DODS) ? yes : no; 1037 | } 1038 | 1039 | return view_as(g_IsSource2009); 1040 | } 1041 | 1042 | stock void AddPrefixAndDefaultColor(char[] sMessage, int iSize, char[] sDefaultColor = "default", char[] sPrefixColor = "prefix") 1043 | { 1044 | if (g_sChatPrefix[0] != '\0' && !g_bIgnorePrefix) { 1045 | Format(sMessage, iSize, "{%s}[%s]{%s} %s", sPrefixColor, g_sChatPrefix, sDefaultColor, sMessage); 1046 | } else { 1047 | Format(sMessage, iSize, "{%s}%s", sDefaultColor, sMessage); 1048 | } 1049 | } 1050 | 1051 | stock void SendPlayerMessage(int iClient, char[] sMessage, int iAuthor = 0) 1052 | { 1053 | if (iAuthor < 1 || iAuthor > MaxClients || !IsClientInGame(iAuthor)) { 1054 | PrintToChat(iClient, sMessage); 1055 | 1056 | if (iAuthor != 0) { 1057 | LogError("Client %d is not valid or in game", iAuthor); 1058 | } 1059 | } else { 1060 | CSayText2(iClient, sMessage, iAuthor); 1061 | } 1062 | } 1063 | --------------------------------------------------------------------------------