#pragma semicolon 1 #include #include #pragma newdecls required #include //#define GFL_API_KEY "secret" #include "GFLClanruAPI.secret" bool g_bLateLoad = false; float g_fMonthlyCosts = 45.0; KeyValues g_Response[MAXPLAYERS + 1]; bool g_bResponseFailed[MAXPLAYERS + 1]; bool g_bClientPreAdminChecked[MAXPLAYERS + 1]; public Plugin myinfo = { name = "GFLCLan.ru API Integration", author = "BotoX", description = "Handles donators.", version = "0.1", url = "" } public void OnPluginStart() { RegConsoleCmd("sm_tier", Command_Tier, "[GFLClan.ru] Displays donator info."); RegConsoleCmd("sm_vip", Command_Tier, "[GFLClan.ru] Displays donator info."); } public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { CreateNative("AsyncHasSteamIDReservedSlot", Native_AsyncHasSteamIDReservedSlot); RegPluginLibrary("GFLClanru"); g_bLateLoad = late; return APLRes_Success; } public void OnRebuildAdminCache(AdminCachePart part) { if(part != AdminCache_Admins) return; CreateTimer(1.0, OnRebuildAdminCachePost, 0, TIMER_FLAG_NO_MAPCHANGE); } public Action OnRebuildAdminCachePost(Handle timer) { for(int client = 1; client <= MaxClients; client++) { if(g_bClientPreAdminChecked[client] && g_Response[client]) OnReceiveUser(client); } return Plugin_Stop; } public void OnClientConnected(int client) { g_bClientPreAdminChecked[client] = false; g_bResponseFailed[client] = false; } public void OnClientAuthorized(int client, const char[] auth) { if(IsFakeClient(client)) return; char sSteam64ID[32]; Steam32IDtoSteam64ID(auth, sSteam64ID, sizeof(sSteam64ID)); int UserSerial = GetClientSerial(client); static char sRequest[256]; FormatEx(sRequest, sizeof(sRequest), "http://direct.gflclan.ru/api/self/Server/OnClientAuthorized?key=%s&steamid=%s", GFL_API_KEY, sSteam64ID); Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); if (!hRequest || !SteamWorks_SetHTTPRequestContextValue(hRequest, UserSerial) || !SteamWorks_SetHTTPCallbacks(hRequest, OnClientAuthorized_OnTransferComplete) || !SteamWorks_SetHTTPRequestHeaderValue(hRequest, "Accept", "application/vdf") || !SteamWorks_SendHTTPRequest(hRequest)) { LogError("%L SteamWorks_CreateHTTPRequest failed.", client); CloseHandle(hRequest); g_bResponseFailed[client] = true; } return; } public int OnClientAuthorized_OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, int UserSerial) { int client = GetClientFromSerial(UserSerial); if(!client) // Player disconnected { CloseHandle(hRequest); return; } if(bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) { LogError("%L OnClientAuthorized HTTP Response failed: %d", client, eStatusCode); CloseHandle(hRequest); g_bResponseFailed[client] = true; if(g_bClientPreAdminChecked[client]) NotifyPostAdminCheck(client); return; } SteamWorks_GetHTTPResponseBodyCallback(hRequest, OnClientAuthorized_APIWebResponse, UserSerial); CloseHandle(hRequest); } public int OnClientAuthorized_APIWebResponse(const char[] sData, int UserSerial) { int client = GetClientFromSerial(UserSerial); if(!client) // Player disconnected return; KeyValues Response = new KeyValues("OnClientAuthorized_APIWebResponse"); if(!Response.ImportFromString(sData, "OnClientAuthorized_APIWebResponse")) { LogError("%L ImportFromString(sData, \"OnClientAuthorized_APIWebResponse\") failed.", client); delete Response; if(g_bClientPreAdminChecked[client]) NotifyPostAdminCheck(client); return; } g_Response[client] = Response; if(g_bClientPreAdminChecked[client]) { LogMessage("%L APIWebResponse late.", client); NotifyPostAdminCheck(client); } } public Action OnClientPreAdminCheck(int client) { g_bClientPreAdminChecked[client] = true; if(g_Response[client] || g_bResponseFailed[client]) return Plugin_Continue; RunAdminCacheChecks(client); return Plugin_Handled; } public void OnClientPostAdminFilter(int client) { OnReceiveUser(client); } public void OnClientDisconnect(int client) { g_bClientPreAdminChecked[client] = false; g_bResponseFailed[client] = false; delete g_Response[client]; } void OnReceiveUser(int client) { KeyValues Response = g_Response[client]; if(!Response) return; ArrayList Groups = new ArrayList(ByteCountToCells(32)); if(Response.JumpToKey("forum")) { int Member = Response.GetNum("member"); if(Member) { int LastSeen = Response.GetNum("last_seen"); int Expires = RoundFloat(LastSeen + 86400.0 * 7.0); int Now = GetTime(); if(Now < Expires) { Groups.PushString("Member"); } } } Response.Rewind(); if(Response.JumpToKey("donations") && Response.GotoFirstSubKey()) { do { char sGroup[32]; Response.GetString("group", sGroup, sizeof(sGroup)); char sState[32]; Response.GetString("state", sState, sizeof(sState)); int Tier = Response.GetNum("tier"); int Deactivate = Response.GetNum("deactivate", 0); if(StrEqual(sState, "active")) { Groups.PushString(sGroup); } else if(Deactivate && (StrEqual(sState, "expired") || StrEqual(sState, "refunded") || StrEqual(sState, "reversed"))) { 0; } } while(Response.GotoNextKey()); } Response.Rewind(); if(!Groups.Length) { delete Groups; return; } AdminId adm; // Use a pre-existing admin if we can if((adm = GetUserAdmin(client)) == INVALID_ADMIN_ID) { LogMessage("Creating new admin for %L", client); adm = CreateAdmin(""); SetUserAdmin(client, adm, true); } for(int i = 0; i < Groups.Length; i++) { char sGroup[32]; Groups.GetString(i, sGroup, sizeof(sGroup)); GroupId grp; if((grp = FindAdmGroup(sGroup)) != INVALID_GROUP_ID) { LogMessage("Adding %L to group %s", client, sGroup); AdminInheritGroup(adm, grp); } else LogError("%L Group %s not found!", client, sGroup); } delete Groups; } public void OnClientPostAdminCheck(int client) { KeyValues Response = g_Response[client]; if(!Response) return; Response.JumpToKey("donations"); Response.GotoFirstSubKey(); do { int Created = Response.GetNum("created"); int Activated = Response.GetNum("activated"); int Expires = Response.GetNum("expires"); int Length = Response.GetNum("length"); char sGroup[32]; Response.GetString("group", sGroup, sizeof(sGroup)); char sState[32]; Response.GetString("state", sState, sizeof(sState)); int Tier = Response.GetNum("tier"); int Anonymous = Response.GetNum("anonymous"); int New = Response.GetNum("new"); int Deactivate = Response.GetNum("deactivate", 0); float fNetAmount = Response.GetFloat("net_amount", 0.0); if(StrEqual(sState, "active")) { static char sExpireDate[32]; FormatTime(sExpireDate, sizeof(sExpireDate), "%a, %d %b %Y %H:%M:%S +00", Expires); int Remaining = Expires - Created; float RemainingDays = Remaining / 3600.0 / 24.0; PrintToChat(client, "\x04[GFLClan.ru]\x01 Donator \x03Tier %d\x01 enabled. Valid until %s (%.1f days)", Tier, sExpireDate, RemainingDays); if(New) { float fDays = fNetAmount * (30.5 / g_fMonthlyCosts); PrintCenterText(client, "Your Tier %d donation has been activated! Thank you <3", Tier); if(fNetAmount && !Anonymous) PrintToChatAll("\x04[GFLClan.ru]\x01 \x03%N\x01's donation paid for %.1f days of server uptime, thanks!", client, fDays); } else if(Remaining < 86400) // less than 24 hours { int Hours = RoundToFloor(Remaining / 3600.0); int Minutes = Remaining % 60; PrintCenterText(client, "Oy vey goyim! Your tier %d donation will expire in %d hours and %d minutes.", Tier, Hours, Minutes); } } else if(StrEqual(sState, "queued")) { float Days = Length / 3600.0 / 24.0; PrintToChat(client, "\x04[GFLClan.ru]\x01 Donator \x03Tier %d\x01 queued. Length: %.1f days", Tier, Days); } else if(StrEqual(sState, "expired")) { PrintCenterText(client, "Oy gevalt goyim! Your tier %d donation has expired.", Tier); } else if(StrEqual(sState, "refunded")) { PrintCenterText(client, "OY GEVALT GOYIM YOUR DONATION HAS BEEN REFUNDED!"); } else if(StrEqual(sState, "reversed")) { PrintCenterText(client, "OY GEVALT GOYIM YOUR DONATION HAS BEEN REVERSED!"); } } while(Response.GotoNextKey()); Response.Rewind(); Response.JumpToKey("forum"); Response.GotoFirstSubKey(); int Member = Response.GetNum("member"); if(Member) { int LastSeen = Response.GetNum("last_seen"); int Expires = RoundFloat(LastSeen + 86400.0 * 7.0); int Now = GetTime(); if(Now < Expires) { static char sExpireDate[32]; FormatTime(sExpireDate, sizeof(sExpireDate), "%a, %d %b %Y %H:%M:%S +00", Expires); PrintToChat(client, "\x04[GFLClan.ru]\x01 \x03Member\x01 enabled. Remember to log in until %s (%.1f days)", sExpireDate, (Expires - Now) / 3600.0 / 24.0); } } } public Action Command_Tier(int client, int args) { KeyValues Response = g_Response[client]; if(!Response) { ReplyToCommand(client, "\x04[GFLClan.ru]\x01 No donator info available!"); return Plugin_Handled; } Response.JumpToKey("donations"); Response.GotoFirstSubKey(); do { int Created = Response.GetNum("created"); int Expires = Response.GetNum("expires"); int Length = Response.GetNum("length"); char sState[32]; Response.GetString("state", sState, sizeof(sState)); int Tier = Response.GetNum("tier"); if(StrEqual(sState, "active")) { static char sExpireDate[32]; FormatTime(sExpireDate, sizeof(sExpireDate), "%a, %d %b %Y %H:%M:%S +00", Expires); int Remaining = Expires - Created; float RemainingDays = Remaining / 3600.0 / 24.0; PrintToChat(client, "\x04[GFLClan.ru]\x01 Donator \x03Tier %d\x01 active. Valid until %s (%.1f days)", Tier, sExpireDate, RemainingDays); } else if(StrEqual(sState, "queued")) { float Days = Length / 3600.0 / 24.0; PrintToChat(client, "\x04[GFLClan.ru]\x01 Donator \x03Tier %d\x01 queued. Length: %.1f days", Tier, Days); } } while(Response.GotoNextKey()); Response.Rewind(); Response.JumpToKey("forum"); Response.GotoFirstSubKey(); int Member = Response.GetNum("member"); if(Member) { int LastSeen = Response.GetNum("last_seen"); int Expires = RoundFloat(LastSeen + 86400.0 * 7.0); int Now = GetTime(); if(Now < Expires) { static char sExpireDate[32]; FormatTime(sExpireDate, sizeof(sExpireDate), "%a, %d %b %Y %H:%M:%S +00", Expires); PrintToChat(client, "\x04[GFLClan.ru]\x01 \x03Member\x01 active. Remember to log in until %s (%.1f days)", sExpireDate, (Expires - Now) / 3600.0 / 24.0); } } return Plugin_Handled; } public int Native_AsyncHasSteamIDReservedSlot(Handle plugin, int numParams) { char sSteam32ID[32]; GetNativeString(1, sSteam32ID, sizeof(sSteam32ID)); AsyncHasSteamIDReservedSlotCallbackFunc Callback; Callback = GetNativeCell(2); any Data; Data = GetNativeCell(3); char sSteam64ID[32]; Steam32IDtoSteam64ID(sSteam32ID, sSteam64ID, sizeof(sSteam64ID)); static char sRequest[256]; FormatEx(sRequest, sizeof(sRequest), "http://direct.gflclan.ru/api/self/Server/HasSteamIDReservedSlot?key=%s&steamid=%s", GFL_API_KEY, sSteam64ID); DataPack Pack = new DataPack(); Pack.WriteString(sSteam32ID); Pack.WriteCell(plugin); Pack.WriteFunction(Callback); Pack.WriteCell(Data); Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); if (!hRequest || !SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 3) || !SteamWorks_SetHTTPRequestContextValue(hRequest, Pack) || !SteamWorks_SetHTTPCallbacks(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferComplete) || !SteamWorks_SendHTTPRequest(hRequest)) { CloseHandle(hRequest); } } public int Native_AsyncHasSteamIDReservedSlot_OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, DataPack Pack) { if(bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) { LogError("Native_AsyncHasSteamIDReservedSlot HTTP Response failed: %d", eStatusCode); CloseHandle(hRequest); // Simulate false response char sData[2] = "0"; Native_AsyncHasSteamIDReservedSlot_APIWebResponse(sData, Pack); return; } SteamWorks_GetHTTPResponseBodyCallback(hRequest, Native_AsyncHasSteamIDReservedSlot_APIWebResponse, Pack); CloseHandle(hRequest); } public int Native_AsyncHasSteamIDReservedSlot_APIWebResponse(char[] sData, DataPack Pack) { Pack.Reset(); char sSteam32ID[32]; Pack.ReadString(sSteam32ID, sizeof(sSteam32ID)); Handle plugin; plugin = Pack.ReadCell(); AsyncHasSteamIDReservedSlotCallbackFunc Callback; Callback = view_as(Pack.ReadFunction()); any Data; Data = Pack.ReadCell(); delete Pack; TrimString(sData); int Result = StringToInt(sData); Call_StartFunction(plugin, Callback); Call_PushString(sSteam32ID); Call_PushCell(Result); Call_PushCell(Data); Call_Finish(); return 0; } stock bool Steam32IDtoSteam64ID(const char[] sSteam32ID, char[] sSteam64ID, int Size) { if(strlen(sSteam32ID) < 11 || strncmp(sSteam32ID[0], "STEAM_", 6)) { sSteam64ID[0] = 0; return false; } int iUpper = 765611979; int isSteam64ID = StringToInt(sSteam32ID[10]) * 2 + 60265728 + sSteam32ID[8] - 48; int iDiv = isSteam64ID / 100000000; int iIdx = 9 - (iDiv ? (iDiv / 10 + 1) : 0); iUpper += iDiv; IntToString(isSteam64ID, sSteam64ID[iIdx], Size - iIdx); iIdx = sSteam64ID[9]; IntToString(iUpper, sSteam64ID, Size); sSteam64ID[9] = iIdx; return true; }