Fix !nominate command

Fix !nominate command

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <mapchooser_redux>
#include <smutils>

ArrayList g_aMapList;
ArrayList g_aOldList;
Menu g_hMapMenu;
int g_iMapFileSerial = -1;
bool g_pStore;
bool g_pShop;

bool g_bPartyblock[MAXPLAYERS+1];

#define MAPSTATUS_ENABLED (1<<0)
#define MAPSTATUS_DISABLED (1<<1)
#define MAPSTATUS_EXCLUDE_CURRENT (1<<2)
#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3)
#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4)

enum struct owner_t
{
    char m_Auth[32];
    char m_Name[32];
}

StringMap g_smOwner;
StringMap g_smState;

public Plugin myinfo =
{
    name        = "Nominations Redux",
    author      = "Kyle",
    description = "Provides Map Nominations",
    version     = MCR_VERSION,
    url         = "https://www.kxnrl.com"
};

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
    return APLRes_Success;
}

public void OnPluginStart()
{
    SMUtils_SetChatPrefix("[\x02M\x04C\x0CR\x01]");
    SMUtils_SetChatSpaces("   ");
    SMUtils_SetChatConSnd(false);
    SMUtils_SetTextDest(HUD_PRINTCENTER);

    LoadTranslations("com.kxnrl.mcr.translations");

    g_aMapList = new ArrayList(ByteCountToCells(128));
    g_aOldList = new ArrayList(ByteCountToCells(128));

    g_smState = new StringMap();
    g_smOwner = new StringMap();

    RegConsoleCmd("nominate",   Command_Nominate);
    RegConsoleCmd("nomination", Command_Nominate);
    RegConsoleCmd("sm_yd",      Command_Nominate);

    RegConsoleCmd("sm_bc",      Command_Partyblock);
    RegConsoleCmd("partyblock", Command_Partyblock);
}

public void OnLibraryAdded(const char[] name)
{
    if (strcmp(name, "store") == 0)
        g_pStore = true;
    else if (strcmp(name, "shop-core") == 0)
        g_pShop = true;
}

public void OnLibraryRemoved(const char[] name)
{
    if (strcmp(name, "store") == 0)
        g_pStore = false;
    else if (strcmp(name, "shop-core") == 0)
        g_pShop = false;
}

public void OnConfigsExecuted()
{
    g_pStore = LibraryExists("store");
    g_pShop = LibraryExists("shop-core");

    if (ReadMapList(g_aMapList, g_iMapFileSerial, "nominations", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) == INVALID_HANDLE)
        if (g_iMapFileSerial == -1)
            SetFailState("Unable to create a valid map list.");

    CreateTimer(90.0, Timer_Broadcast, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}

public void OnMapEnd()
{
    char map[128];
    GetCurrentMap(map, 128);
    g_smOwner.Remove(map);
}

public void OnNominationRemoved(const char[] map, int owner)
{
    int status;

    if (!g_smState.GetValue(map, status))
        return;

    if ((status & MAPSTATUS_EXCLUDE_NOMINATED) != MAPSTATUS_EXCLUDE_NOMINATED)
        return;

    g_smState.SetValue(map, MAPSTATUS_ENABLED);
}

public Action Command_Nominate(int client, int args)
{
    if (!client)
        return Plugin_Handled;

    if (!IsNominateAllowed(client))
        return Plugin_Handled;

    g_bPartyblock[client] = false;

    if (args < 1)
    {
        AttemptNominate(client);
        return Plugin_Handled;
    }

    char map[32];
    GetCmdArg(1, map, 32);
    if (strlen(map) >= 3)
    {
        FuzzyNominate(client, map);
        return Plugin_Handled;
    }

    return Plugin_Handled;
}

public Action Command_Partyblock(int client, int args)
{
    if (!client)
        return Plugin_Handled;

    if (!IsNominateAllowed(client))
        return Plugin_Handled;

    g_bPartyblock[client] = true;

    if (args < 1)
    {
        AttemptNominate(client);
        return Plugin_Handled;
    }

    char map[32];
    GetCmdArg(1, map, 32);
    if (strlen(map) >= 3)
    {
        FuzzyNominate(client, map);
        return Plugin_Handled;
    }

    return Plugin_Handled;
}

public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs)
{
    if (!client || sArgs[0] == '!' || sArgs[0] == '/' || sArgs[0] == '.')
        return;

    if (StrContains(sArgs, "nominat", false) == -1 && strcmp(sArgs, "nextmap", false) != 0)
        return;

    if (!IsNominateAllowed(client))
        return;

    g_bPartyblock[client] = false;
    AttemptNominate(client);
}

void FuzzyNominate(int client, const char[] find)
{
    ArrayList result = new ArrayList(ByteCountToCells(128));

    char map[128];
    for(int x = 0; x < g_aMapList.Length; ++x)
    {
        g_aMapList.GetString(x, map, 128);
        if (StrContains(map, find, false) > -1)
            result.PushString(map);
    }

    if (result.Length == 0)
    {
        delete result;
        Chat(client, "%T", "NominateResult_NoMatch", client, find);
        AttemptNominate(client);
        return;
    }

    bool desctag = FindConVar("mcr_include_desctag").BoolValue;
    bool nametag = FindConVar("mcr_include_nametag").BoolValue;

    Menu menu = new Menu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);

    char desc[128];
    for(int x = 0; x < result.Length; ++x)
    {
        result.GetString(x, map, 128);
        menu.AddItem(map, desctag && GetMapDescEx(map, desc, 128, true, nametag, (g_pStore || g_pShop)) ? desc : map);
    }

    menu.SetTitle("%T", "fuzzy title", client, menu.ItemCount, find, g_bPartyblock[client] ? "partyblock nominate menu item" : "nominate nominate menu item", client);

    menu.Display(client, MENU_TIME_FOREVER);

    delete result;
}

void AttemptNominate(int client)
{
    g_hMapMenu.SetTitle("%T\n ", g_bPartyblock[client] ? "partyblock menu title" : "nominate menu title", client);
    g_hMapMenu.Display(client, MENU_TIME_FOREVER);
}

void BuildMapMenu()
{
    if (g_hMapMenu != null)
    {
        delete g_hMapMenu;
        g_hMapMenu = null;
    }

    g_smState.Clear();

    g_hMapMenu = new Menu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);

    g_aOldList.Clear();
    GetExcludeMapList(g_aOldList);

    char currentMap[64];
    GetCurrentMap(currentMap, 64);

    bool desctag = FindConVar("mcr_include_desctag").BoolValue;
    bool nametag = FindConVar("mcr_include_nametag").BoolValue;

    char desc[128], map[128]; Nominations n;
    for(int i = 0; i < g_aMapList.Length; i++)
    {
        int status = MAPSTATUS_ENABLED;

        g_aMapList.GetString(i, map, 128);

        if (strcmp(map, currentMap) == 0)
            status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_CURRENT;
        else if (GetNominated(map, n))
        {
            owner_t owner;
            GetClientAuthId(n.m_Owner, AuthId_Steam2, owner.m_Auth, 32, false);
            GetClientName(n.m_Owner, owner.m_Name, 32);
            g_smOwner.SetArray(map, owner, sizeof(owner_t), true);
            status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED;
        }
        else
        {
            if (g_aOldList.FindString(map) != -1)
            status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS;
        }

        g_hMapMenu.AddItem(map, desctag && GetMapDescEx(map, desc, 128, true, nametag, (g_pStore || g_pShop)) ? desc : map);
        g_smState.SetValue(map, status);
    }

    g_hMapMenu.ExitButton = true;
}

public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int param2)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char map[128];
            menu.GetItem(param2, map, 128);

            NominateResult result = NominateMap(map, false, param1, g_bPartyblock[param1]);

            if (result == NominateResult_NoCredits)
            {
                Chat(param1, "%T", "NominateResult_NoCredits", param1, map);
                return 0;
            }

            if (result == NominateResult_InvalidMap)
            {
                Chat(param1, "%T", "NominateResult_InvalidMap", param1, map);
                return 0;
            }

            if (result == NominateResult_AlreadyInVote)
            {
                Chat(param1, "%T", "NominateResult_AlreadyInVote", param1);
                return 0;
            }

            if (result == NominateResult_VoteFull)
            {
                Chat(param1, "%T", "NominateResult_VoteFull", param1);
                return 0;
            }

            int min, max; bool vip, adm;
            GetMapPermission(map, vip, adm, min, max);

            if (result == NominateResult_AdminOnly)
            {
                Chat(param1, "%T", "NominateResult_AdminOnly", param1);
                return 0;
            }

            if (result == NominateResult_VIPOnly)
            {
                Chat(param1, "%T", "NominateResult_VIPOnly", param1);
                return 0;
            }

            if (result == NominateResult_CertainTimes)
            {
                Chat(param1, "%T", "NominateResult_CertainTimes", param1);
                return 0;
            }

            if (result == NominateResult_MinPlayers)
            {
                Chat(param1, "%T", "NominateResult_MinPlayers", param1, min);
                return 0;
            }

            if (result == NominateResult_MaxPlayers)
            {
                Chat(param1, "%T", "NominateResult_MaxPlayers", param1, max);
                return 0;
            }

            if (result == NominateResult_RecentlyPlayed)
            {
                Chat(param1, "%T", "NominateResult_RecentlyPlayed", param1);
                return 0;
            }

            if (result == NominateResult_PartyBlock)
            {
                Chat(param1, "%T", "NominateResult_PartyBlock", param1);
                return 0;
            }

            if (result == NominateResult_PartyBlockDisabled)
            {
                Chat(param1, "%T", "NominateResult_PartyBlockDisabled", param1);
                return 0;
            }

            owner_t owner;
            GetClientAuthId(param1, AuthId_Steam2, owner.m_Auth, 32, true);
            GetClientName(param1, owner.m_Name, 32);
            g_smOwner.SetArray(map, owner, sizeof(owner_t), true);
            g_smState.SetValue(map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED, true);

            if (result == NominateResult_PartyBlockAdded)
            {
                tChatAll("%t", "nominate partyblock map", param1, map);
                LogMessage("[MCR]  \"%L\" partyblock %s", param1, map);
            }
            else
            {
                if (result == NominateResult_Replaced)
                    tChatAll("%t", "nominate changed map", param1, map);
                else
                    tChatAll("%t", "nominate nominate map", param1, map);

                LogMessage("[MCR]  \"%L\" nominated %s", param1, map);
            }

            char desc[128];
            if (GetMapDesc(map, desc, 128))
            {
                ChatAll("\x0A -> \x0E[\x05%s\x0E]", desc);
            }
        }

        case MenuAction_DrawItem:
        {
            char map[128];
            menu.GetItem(param2, map, 128);

            int status;

            if (!g_smState.GetValue(map, status))
            {
                LogError("case MenuAction_DrawItem: Menu selection of item not in trie. Major logic problem somewhere.");
                return ITEMDRAW_DISABLED;
            }

            if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
                return ITEMDRAW_DISABLED;

            int min, max; // players = GetClientCount(false);
            bool adm, vip;
            if (!GetMapPermission(map, vip, adm, min, max))
                return ITEMDRAW_DISABLED;

            // players?
            //if ((max > 0 && players >= max) || (min > 0 && players < min))
            //    return ITEMDRAW_DISABLED;

            // admin or vip
            if ((adm && !IsClientAdmin(param1)) || (vip && !IsClientVIP(param1)))
                return ITEMDRAW_DISABLED;

            if (IsNominated(map))
                return ITEMDRAW_DISABLED;

            return ITEMDRAW_DEFAULT;
        }

        case MenuAction_DisplayItem:
        {
            char map[128], display[150];
            menu.GetItem(param2, map, 128, _, display, 150);

            int status;

            if (!g_smState.GetValue(map, status))
            {
                LogError("case MenuAction_DisplayItem: Menu selection of item not in trie. Major logic problem somewhere.");
                return 0;
            }

            char trans[128];
            GetMapDescEx(map, trans, 128, false, false, false);
            if (g_pStore || g_pShop)
            {
                Format(trans, 128, "%s [%T: %d]", trans, g_bPartyblock[param1] ? "partyblock nominate menu item" : "nominate nominate menu item", param1, GetMapPrice(map, true, g_bPartyblock[param1]));
            }

            if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
            {
                if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT)
                {
                    Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu current Map", param1);
                    return RedrawMenuItem(display);
                }

                if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS)
                {
                    Format(display, sizeof(display), "%s\n%s (CD: %5d)", map, trans, GetMapCooldown(map));
                    return RedrawMenuItem(display);
                }

                if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED)
                {
                    owner_t owner;
                    if (g_smOwner.GetArray(map, owner, sizeof(owner_t)))
                         Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu was nominated name", param1, owner.m_Name);
                    else Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu was nominated"     , param1);
                    return RedrawMenuItem(display);
                }
            }

            Nominations n;
            if (GetNominated(map, n))
            {
                char name[32];
                GetClientName(n.m_Owner, name, 32);
                Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu was nominated name", param1, name);
                return RedrawMenuItem(display);
            }

            Format(display, sizeof(display), "%s\n%s", map, trans);
            return RedrawMenuItem(display);
        }

        case MenuAction_End:
        {
            if (menu != g_hMapMenu)
                delete menu;
        }
    }

    return 0;
}

stock bool IsNominateAllowed(int client)
{
    CanNominateResult result = CanNominate();

    switch(result)
    {
        case CanNominate_No_VoteInProgress:
        {
            Chat(client, "%T", "nominate vote in progress", client);
            return false;
        }

        case CanNominate_No_VoteComplete:
        {
            char map[128];
            GetNextMap(map, 128);
            Chat(client, "%T", "nominate vote complete", client, map);
            return false;
        }

        case CanNominate_No_VoteFull:
        {
            Chat(client, "%T", "nominate full vote", client);
            return false;
        }

        case CanNominate_No_PartyBlock:
        {
            Chat(client, "%T", "nominate partyblock", client);
            return false;
        }
    }

    return true;
}

public void OnMapVotePoolChanged()
{
    BuildMapMenu();
}

public Action Timer_Broadcast(Handle timer)
{
    char map[128];
    GetCurrentMap(map, 128);

    owner_t owner;
    if (!g_smOwner.GetArray(map, owner, sizeof(owner_t)))
        return Plugin_Stop;

    int client = FindClientByAuth(owner.m_Auth);

    if (!client)
        tChatAll("%t", "nominated by name", owner.m_Name, owner.m_Auth[8]);
    else
        tChatAll("%t", "nominated by client", client, owner.m_Auth[8]);

    return Plugin_Continue;
}

int FindClientByAuth(const char[] steamid)
{
    char m_szAuth[32];
    for(int client = 1; client <= MaxClients; ++client)
        if (IsClientAuthorized(client))
            if (GetClientAuthId(client, AuthId_Steam2, m_szAuth, 32, true))
                if (StrEqual(m_szAuth, steamid))
                    return client;

    return 0;
}

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <mapchooser_redux>
#include <smutils>

ArrayList g_aMapList;
ArrayList g_aOldList;
Menu g_hMapMenu;
int g_iMapFileSerial = -1;
bool g_pStore;
bool g_pShop;

bool g_bPartyblock[MAXPLAYERS+1];

#define MAPSTATUS_ENABLED (1<<0)
#define MAPSTATUS_DISABLED (1<<1)
#define MAPSTATUS_EXCLUDE_CURRENT (1<<2)
#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3)
#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4)

enum struct owner_t
{
    char m_Auth[32];
    char m_Name[32];
}

StringMap g_smOwner;
StringMap g_smState;

public Plugin myinfo =
{
    name        = "Nominations Redux",
    author      = "Kyle",
    description = "Provides Map Nominations",
    version     = MCR_VERSION,
    url         = "https://www.kxnrl.com"
};

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
    return APLRes_Success;
}

public void OnPluginStart()
{
    SMUtils_SetChatPrefix("[\x02M\x04C\x0CR\x01]");
    SMUtils_SetChatSpaces("   ");
    SMUtils_SetChatConSnd(false);
    SMUtils_SetTextDest(HUD_PRINTCENTER);

    LoadTranslations("com.kxnrl.mcr.translations");

    g_aMapList = new ArrayList(ByteCountToCells(128));
    g_aOldList = new ArrayList(ByteCountToCells(128));

    g_smState = new StringMap();
    g_smOwner = new StringMap();

    RegConsoleCmd("nominate",   Command_Nominate);
    RegConsoleCmd("nomination", Command_Nominate);
    RegConsoleCmd("sm_yd",      Command_Nominate);

    RegConsoleCmd("sm_bc",      Command_Partyblock);
    RegConsoleCmd("partyblock", Command_Partyblock);
}

public void OnLibraryAdded(const char[] name)
{
    if (strcmp(name, "store") == 0)
        g_pStore = true;
    else if (strcmp(name, "shop-core") == 0)
        g_pShop = true;
}

public void OnLibraryRemoved(const char[] name)
{
    if (strcmp(name, "store") == 0)
        g_pStore = false;
    else if (strcmp(name, "shop-core") == 0)
        g_pShop = false;
}

public void OnConfigsExecuted()
{
    g_pStore = LibraryExists("store");
    g_pShop = LibraryExists("shop-core");

    if (ReadMapList(g_aMapList, g_iMapFileSerial, "nominations", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) == INVALID_HANDLE)
        if (g_iMapFileSerial == -1)
            SetFailState("Unable to create a valid map list.");

    CreateTimer(90.0, Timer_Broadcast, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}

public void OnMapEnd()
{
    char map[128];
    GetCurrentMap(map, 128);
    g_smOwner.Remove(map);
}

public void OnNominationRemoved(const char[] map, int owner)
{
    int status;

    if (!g_smState.GetValue(map, status))
        return;

    if ((status & MAPSTATUS_EXCLUDE_NOMINATED) != MAPSTATUS_EXCLUDE_NOMINATED)
        return;

    g_smState.SetValue(map, MAPSTATUS_ENABLED);
}

public Action Command_Nominate(int client, int args)
{
    if (!client)
        return Plugin_Handled;

    if (!IsNominateAllowed(client))
        return Plugin_Handled;

    g_bPartyblock[client] = false;

    if (args < 1)
    {
        AttemptNominate(client);
        return Plugin_Handled;
    }

    char map[32];
    GetCmdArg(1, map, 32);
    if (strlen(map) >= 3)
    {
        FuzzyNominate(client, map);
        return Plugin_Handled;
    }

    return Plugin_Handled;
}

public Action Command_Partyblock(int client, int args)
{
    if (!client)
        return Plugin_Handled;

    if (!IsNominateAllowed(client))
        return Plugin_Handled;

    g_bPartyblock[client] = true;

    if (args < 1)
    {
        AttemptNominate(client);
        return Plugin_Handled;
    }

    char map[32];
    GetCmdArg(1, map, 32);
    if (strlen(map) >= 3)
    {
        FuzzyNominate(client, map);
        return Plugin_Handled;
    }

    return Plugin_Handled;
}

public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs)
{
    if (!client)
        return;

    if (StrContains(sArgs, "nominat", false) == -1 && strcmp(sArgs, "nextmap", false) != 0)
        return;

    if (!IsNominateAllowed(client))
        return;

    g_bPartyblock[client] = false;
    AttemptNominate(client);
}

void FuzzyNominate(int client, const char[] find)
{
    ArrayList result = new ArrayList(ByteCountToCells(128));

    char map[128];
    for(int x = 0; x < g_aMapList.Length; ++x)
    {
        g_aMapList.GetString(x, map, 128);
        if (StrContains(map, find, false) > -1)
            result.PushString(map);
    }

    if (result.Length == 0)
    {
        delete result;
        Chat(client, "%T", "NominateResult_NoMatch", client, find);
        AttemptNominate(client);
        return;
    }

    bool desctag = FindConVar("mcr_include_desctag").BoolValue;
    bool nametag = FindConVar("mcr_include_nametag").BoolValue;

    Menu menu = new Menu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);

    char desc[128];
    for(int x = 0; x < result.Length; ++x)
    {
        result.GetString(x, map, 128);
        menu.AddItem(map, desctag && GetMapDescEx(map, desc, 128, true, nametag, (g_pStore || g_pShop)) ? desc : map);
    }

    menu.SetTitle("%T", "fuzzy title", client, menu.ItemCount, find, g_bPartyblock[client] ? "partyblock nominate menu item" : "nominate nominate menu item", client);

    menu.Display(client, MENU_TIME_FOREVER);

    delete result;
}

void AttemptNominate(int client)
{
    g_hMapMenu.SetTitle("%T\n ", g_bPartyblock[client] ? "partyblock menu title" : "nominate menu title", client);
    g_hMapMenu.Display(client, MENU_TIME_FOREVER);
}

void BuildMapMenu()
{
    if (g_hMapMenu != null)
    {
        delete g_hMapMenu;
        g_hMapMenu = null;
    }

    g_smState.Clear();

    g_hMapMenu = new Menu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);

    g_aOldList.Clear();
    GetExcludeMapList(g_aOldList);

    char currentMap[64];
    GetCurrentMap(currentMap, 64);

    bool desctag = FindConVar("mcr_include_desctag").BoolValue;
    bool nametag = FindConVar("mcr_include_nametag").BoolValue;

    char desc[128], map[128]; Nominations n;
    for(int i = 0; i < g_aMapList.Length; i++)
    {
        int status = MAPSTATUS_ENABLED;

        g_aMapList.GetString(i, map, 128);

        if (strcmp(map, currentMap) == 0)
            status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_CURRENT;
        else if (GetNominated(map, n))
        {
            owner_t owner;
            GetClientAuthId(n.m_Owner, AuthId_Steam2, owner.m_Auth, 32, false);
            GetClientName(n.m_Owner, owner.m_Name, 32);
            g_smOwner.SetArray(map, owner, sizeof(owner_t), true);
            status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED;
        }
        else
        {
            if (g_aOldList.FindString(map) != -1)
            status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS;
        }

        g_hMapMenu.AddItem(map, desctag && GetMapDescEx(map, desc, 128, true, nametag, (g_pStore || g_pShop)) ? desc : map);
        g_smState.SetValue(map, status);
    }

    g_hMapMenu.ExitButton = true;
}

public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int param2)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char map[128];
            menu.GetItem(param2, map, 128);

            NominateResult result = NominateMap(map, false, param1, g_bPartyblock[param1]);

            if (result == NominateResult_NoCredits)
            {
                Chat(param1, "%T", "NominateResult_NoCredits", param1, map);
                return 0;
            }

            if (result == NominateResult_InvalidMap)
            {
                Chat(param1, "%T", "NominateResult_InvalidMap", param1, map);
                return 0;
            }

            if (result == NominateResult_AlreadyInVote)
            {
                Chat(param1, "%T", "NominateResult_AlreadyInVote", param1);
                return 0;
            }

            if (result == NominateResult_VoteFull)
            {
                Chat(param1, "%T", "NominateResult_VoteFull", param1);
                return 0;
            }

            int min, max; bool vip, adm;
            GetMapPermission(map, vip, adm, min, max);

            if (result == NominateResult_AdminOnly)
            {
                Chat(param1, "%T", "NominateResult_AdminOnly", param1);
                return 0;
            }

            if (result == NominateResult_VIPOnly)
            {
                Chat(param1, "%T", "NominateResult_VIPOnly", param1);
                return 0;
            }

            if (result == NominateResult_CertainTimes)
            {
                Chat(param1, "%T", "NominateResult_CertainTimes", param1);
                return 0;
            }

            if (result == NominateResult_MinPlayers)
            {
                Chat(param1, "%T", "NominateResult_MinPlayers", param1, min);
                return 0;
            }

            if (result == NominateResult_MaxPlayers)
            {
                Chat(param1, "%T", "NominateResult_MaxPlayers", param1, max);
                return 0;
            }

            if (result == NominateResult_RecentlyPlayed)
            {
                Chat(param1, "%T", "NominateResult_RecentlyPlayed", param1);
                return 0;
            }

            if (result == NominateResult_PartyBlock)
            {
                Chat(param1, "%T", "NominateResult_PartyBlock", param1);
                return 0;
            }

            if (result == NominateResult_PartyBlockDisabled)
            {
                Chat(param1, "%T", "NominateResult_PartyBlockDisabled", param1);
                return 0;
            }

            owner_t owner;
            GetClientAuthId(param1, AuthId_Steam2, owner.m_Auth, 32, true);
            GetClientName(param1, owner.m_Name, 32);
            g_smOwner.SetArray(map, owner, sizeof(owner_t), true);
            g_smState.SetValue(map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED, true);

            if (result == NominateResult_PartyBlockAdded)
            {
                tChatAll("%t", "nominate partyblock map", param1, map);
                LogMessage("[MCR]  \"%L\" partyblock %s", param1, map);
            }
            else
            {
                if (result == NominateResult_Replaced)
                    tChatAll("%t", "nominate changed map", param1, map);
                else
                    tChatAll("%t", "nominate nominate map", param1, map);

                LogMessage("[MCR]  \"%L\" nominated %s", param1, map);
            }

            char desc[128];
            if (GetMapDesc(map, desc, 128))
            {
                ChatAll("\x0A -> \x0E[\x05%s\x0E]", desc);
            }
        }

        case MenuAction_DrawItem:
        {
            char map[128];
            menu.GetItem(param2, map, 128);

            int status;

            if (!g_smState.GetValue(map, status))
            {
                LogError("case MenuAction_DrawItem: Menu selection of item not in trie. Major logic problem somewhere.");
                return ITEMDRAW_DISABLED;
            }

            if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
                return ITEMDRAW_DISABLED;

            int min, max; // players = GetClientCount(false);
            bool adm, vip;
            if (!GetMapPermission(map, vip, adm, min, max))
                return ITEMDRAW_DISABLED;

            // players?
            //if ((max > 0 && players >= max) || (min > 0 && players < min))
            //    return ITEMDRAW_DISABLED;

            // admin or vip
            if ((adm && !IsClientAdmin(param1)) || (vip && !IsClientVIP(param1)))
                return ITEMDRAW_DISABLED;

            if (IsNominated(map))
                return ITEMDRAW_DISABLED;

            return ITEMDRAW_DEFAULT;
        }

        case MenuAction_DisplayItem:
        {
            char map[128], display[150];
            menu.GetItem(param2, map, 128, _, display, 150);

            int status;

            if (!g_smState.GetValue(map, status))
            {
                LogError("case MenuAction_DisplayItem: Menu selection of item not in trie. Major logic problem somewhere.");
                return 0;
            }

            char trans[128];
            GetMapDescEx(map, trans, 128, false, false, false);
            if (g_pStore || g_pShop)
            {
                Format(trans, 128, "%s [%T: %d]", trans, g_bPartyblock[param1] ? "partyblock nominate menu item" : "nominate nominate menu item", param1, GetMapPrice(map, true, g_bPartyblock[param1]));
            }

            if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
            {
                if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT)
                {
                    Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu current Map", param1);
                    return RedrawMenuItem(display);
                }

                if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS)
                {
                    Format(display, sizeof(display), "%s\n%s (CD: %5d)", map, trans, GetMapCooldown(map));
                    return RedrawMenuItem(display);
                }

                if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED)
                {
                    owner_t owner;
                    if (g_smOwner.GetArray(map, owner, sizeof(owner_t)))
                         Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu was nominated name", param1, owner.m_Name);
                    else Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu was nominated"     , param1);
                    return RedrawMenuItem(display);
                }
            }

            Nominations n;
            if (GetNominated(map, n))
            {
                char name[32];
                GetClientName(n.m_Owner, name, 32);
                Format(display, sizeof(display), "%s\n%s (%T)", map, trans, "nominate menu was nominated name", param1, name);
                return RedrawMenuItem(display);
            }

            Format(display, sizeof(display), "%s\n%s", map, trans);
            return RedrawMenuItem(display);
        }

        case MenuAction_End:
        {
            if (menu != g_hMapMenu)
                delete menu;
        }
    }

    return 0;
}

stock bool IsNominateAllowed(int client)
{
    CanNominateResult result = CanNominate();

    switch(result)
    {
        case CanNominate_No_VoteInProgress:
        {
            Chat(client, "%T", "nominate vote in progress", client);
            return false;
        }

        case CanNominate_No_VoteComplete:
        {
            char map[128];
            GetNextMap(map, 128);
            Chat(client, "%T", "nominate vote complete", client, map);
            return false;
        }

        case CanNominate_No_VoteFull:
        {
            Chat(client, "%T", "nominate full vote", client);
            return false;
        }

        case CanNominate_No_PartyBlock:
        {
            Chat(client, "%T", "nominate partyblock", client);
            return false;
        }
    }

    return true;
}

public void OnMapVotePoolChanged()
{
    BuildMapMenu();
}

public Action Timer_Broadcast(Handle timer)
{
    char map[128];
    GetCurrentMap(map, 128);

    owner_t owner;
    if (!g_smOwner.GetArray(map, owner, sizeof(owner_t)))
        return Plugin_Stop;

    int client = FindClientByAuth(owner.m_Auth);

    if (!client)
        tChatAll("%t", "nominated by name", owner.m_Name, owner.m_Auth[8]);
    else
        tChatAll("%t", "nominated by client", client, owner.m_Auth[8]);

    return Plugin_Continue;
}

int FindClientByAuth(const char[] steamid)
{
    char m_szAuth[32];
    for(int client = 1; client <= MaxClients; ++client)
        if (IsClientAuthorized(client))
            if (GetClientAuthId(client, AuthId_Steam2, m_szAuth, 32, true))
                if (StrEqual(m_szAuth, steamid))
                    return client;

    return 0;
}


Have httprequest build route

Have httprequest build route

/**
 * vim: set ts=4 :
 * =============================================================================
 * map_votes
 * Interact with the Map Votes web api - https://github.com/CrimsonTautology/map_votes
 *
 * Copyright 2013 The Crimson Tautology
 * =============================================================================
 *
 */

#pragma semicolon 1

#include <sourcemod>
#include <steamtools>
#include <base64>
#include <smjansson>

#define PLUGIN_VERSION "0.1"

public Plugin:myinfo = {
    name = "MapVotes",
    author = "CrimsonTautology",
    description = "Interact with the Map Votes web api",
    version = PLUGIN_VERSION,
    url = "https://github.com/CrimsonTautology/sm_map_votes"
};

#define CAST_VOTE_ROUTE "/v1/api/cast_vote"
#define WRITE_MESSAGE_ROUTE "/v1/api/write_message"
#define FAVORITE_ROUTE "/v1/api/favorite"
#define UNFAVORITE_ROUTE "/v1/api/unfavorite"
#define GET_FAVORITES_ROUTE "/v1/api/get_favorites"
#define HAVE_NOT_VOTED_ROUTE "/v1/api/have_not_voted"
#define SERVER_QUERY_ROUTE "/v1/api/server_query"
#define MAPS_ROUTE "/maps"

#define MAX_STEAMID_LENGTH 21
#define MAX_COMMUNITYID_LENGTH 18

new Handle:g_Cvar_MapVotesUrl = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesPort = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesApiKey = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesVotingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesCommentingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesNominationsName = INVALID_HANDLE;

new g_MapFileSerial = -1;
new Handle:g_MapList = INVALID_HANDLE;
new Handle:g_MapTrie = INVALID_HANDLE;

new Handle:g_nominations = INVALID_HANDLE;
new Function:g_Handler_MapSelectMenu = INVALID_FUNCTION;

public OnPluginStart()
{

    g_Cvar_MapVotesUrl = CreateConVar("sm_map_votes_url", "", "URL to your Map Votes web page");
    g_Cvar_MapVotesPort = CreateConVar("sm_map_votes_port", "80", "HTTP Port used");
    g_Cvar_MapVotesApiKey = CreateConVar("sm_map_votes_api_key", "", "The API key you generated to interact with the Map Votes web page");
    g_Cvar_MapVotesVotingEnabled = CreateConVar("sm_map_votes_voting_enabled", "1", "Whether players are allowed to vote on the current map");
    g_Cvar_MapVotesCommentingEnabled = CreateConVar("sm_map_votes_commenting_enabled", "1", "Whether players are allowed to comment on the current map");
    g_Cvar_MapVotesNominationsName = CreateConVar("sm_map_votes_nominations_plugin", "nominations.smx", "The nominations plugin used by the server");

    RegConsoleCmd("sm_votemenu", Command_VoteMenu, "Bring up a menu to vote on the current map");
    RegConsoleCmd("sm_voteup", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_like", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_votedown", Command_VoteDown, "Vote that you hate the current map");
    RegConsoleCmd("sm_hate", Command_VoteDown, "Vote that you hate the current map");

    RegConsoleCmd("sm_fav", Command_Favorite, "Add this map to your favorites");
    RegConsoleCmd("sm_unfav", Command_Unfavorite, "Remove this map to your favorites");
    RegConsoleCmd("sm_nomfav", Command_GetFavorites, "Nominate from a list of your favorites");

    RegConsoleCmd("sm_mapcomment", Command_MapComment, "Comment on the current map");
    RegConsoleCmd("sm_mc", Command_MapComment, "Comment on the current map");

    RegConsoleCmd("sm_viewmap", Command_ViewMap, "View the Map Votes web page for this map");

    RegAdminCmd("sm_have_not_voted", Command_HaveNotVoted, ADMFLAG_VOTE, "Popup a vote panel to every player on the server that has not yet voted on this map");

    RegConsoleCmd("test", Test);

    new array_size = ByteCountToCells(PLATFORM_MAX_PATH);
    g_MapList = CreateArray(array_size);
    g_MapTrie = CreateTrie();
}

public OnConfigsExecuted()
{
    BuildMapListAndTrie();
}

public OnAllPluginsLoaded() {
    new String:noms[PLATFORM_MAX_PATH];
    GetConVarString(g_Cvar_MapVotesNominationsName, noms, sizeof(noms));
    //g_nominations = FindPluginByFile(noms);
    g_nominations = FindPluginByFile("nominations.smx");

    //Check if nominations.smx is both available and currently running
    if(g_nominations == INVALID_HANDLE || GetPluginStatus(g_nominations) != Plugin_Running){
        SetFailState("[MapVotes] Error, nominations is currently not running");
    }
    else{
        //We should be clear to link the MapSelectMenu function
        g_Handler_MapSelectMenu = GetFunctionByName(g_nominations, "Handler_MapSelectMenu");
    }
}

public Action:Command_VoteMenu(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        CallVoteOnClient(client);
    }

    return Plugin_Handled;
}

public Action:Command_VoteUp(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        CastVote(client, 1);
    }

    return Plugin_Handled;
}

public Action:Command_VoteDown(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        CastVote(client, -1);
    }

    return Plugin_Handled;
}

public Action:Command_Favorite(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, true);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, FavoriteSearchHandler);
        }
    }

    return Plugin_Handled;
}
public Action:Command_Unfavorite(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, false);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, UnfavoriteSearchHandler);
        }

    }

    return Plugin_Handled;
}
public Action:Command_GetFavorites(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        GetFavorites(client);
    }

    return Plugin_Handled;
}

public Action:Command_ViewMap(client, args)
{
    if(client && IsClientAuthorized(client)){
        ViewMap(client);
    }

    return Plugin_Handled;
}

public Action:Command_MapComment(client, args)
{
    if (!GetConVarBool(g_Cvar_MapVotesCommentingEnabled))
    {
        return Plugin_Handled;
    }

    if (args < 1)
    {
        ReplyToCommand(client, "[MapVotes] Usage: !map_comment <comment>");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        decl String:comment[256];
        GetCmdArgString(comment, sizeof(comment));
        WriteMessage(client, comment);
    }

    return Plugin_Handled;
}

public Action:Command_HaveNotVoted(client, args)
{
    HaveNotVoted();

    return Plugin_Handled;
}

BuildMapListAndTrie()
{
    //Build the map list
    if (ReadMapList(g_MapList,
                g_MapFileSerial,
                "nominations",
                MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER)
            == INVALID_HANDLE)
    {
        if (g_MapFileSerial == -1)
        {
            SetFailState("Unable to create a valid map list.");
        }else{
            //Build the map trie; note we don't care about the value, just if the map exists in the trie
            ClearTrie(g_MapTrie);
            new String:map[PLATFORM_MAX_PATH];

            for (new i = 0; i < GetArraySize(g_MapList); i++)
            {
                GetArrayString(g_MapList, i, map, sizeof(map));
                SetTrieValue(g_MapTrie, map, 1);
            }
        }
    }
}

//By 11530
//GetSteamAccountID(client) does not work because we don't have 64 bit types
//TODO - maybe deprecated
stock bool:GetCommunityIDString(const String:SteamID[], String:CommunityID[], const CommunityIDSize)
{
    decl String:SteamIDParts[3][11];
    new const String:Identifier[] = "76561197960265728";

    if ((CommunityIDSize < 1) || (ExplodeString(SteamID, ":", SteamIDParts, sizeof(SteamIDParts), sizeof(SteamIDParts[])) != 3))
    {
        CommunityID[0] = '\0';
        return false;
    }

    new Current, CarryOver = (SteamIDParts[1][0] == '1');
    for (new i = (CommunityIDSize - 2), j = (strlen(SteamIDParts[2]) - 1), k = (strlen(Identifier) - 1); i >= 0; i--, j--, k--)
    {
        Current = (j >= 0 ? (2 * (SteamIDParts[2][j] - '0')) : 0) + CarryOver + (k >= 0 ? ((Identifier[k] - '0') * 1) : 0);
        CarryOver = Current / 10;
        CommunityID[i] = (Current % 10) + '0';
    }

    CommunityID[CommunityIDSize - 1] = '\0';
    return true;
}

public AddAccessCode(&HTTPRequestHandle:request)
{
    decl String:api_key[128];
    GetConVarString(g_Cvar_MapVotesApiKey, api_key, sizeof(api_key));
	Steam_SetHTTPRequestGetOrPostParameter(request, "access_token", api_key);
}

public HTTPRequestHandle:CreateMapVotesRequest(const String:route[])
{
    //TODO - check for forward slash after base_url;
    decl String:base_url[256], String:url[512];
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));
    Format(url, sizeof(url),
            "%s%s", base_url, route);

    //ReplaceString(base_url, sizeof(base_url), "http://", "", false);
    //ReplaceString(base_url, sizeof(base_url), "https://", "", false);
    return Steam_CreateHTTPRequest(HTTPMethod_POST, url);

}
public Steam_SetHTTPRequestGetOrPostParameterInt(&HTTPRequestHandle:request, const String:param[], value)
{
    String[64] tmp;
    IntToString(tmp, paramValue);
    Steam_SetHTTPRequestGetOrPostParameter(request, param, tmp);
}

public WriteMessage(client, String:message[256])
{
    //Encode the message to be url safe
    decl String:base64[256], String:base64_url[256];
    EncodeBase64(base64, sizeof(base64), message);
    Base64MimeToUrl(base64_url, sizeof(base64_url), base64);

    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    GetClientAuthString(client, buffer, sizeof(buffer));
    //TODO Steam_GetCSteamIDForClient?
    GetCommunityIDString(buffer, uid, sizeof(uid));

    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));

    new HTTPRequestHandle:request = CreateMapVotesRequest(WRITE_MESSAGE_ROUTE);
	Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
	Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
	Steam_SetHTTPRequestGetOrPostParameter(request, "comment", base64_url);
	Steam_SetHTTPRequestGetOrPostParameter(request, "base64", "1");
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveWriteMessage, GetClientUserId(client));

    //MapVotesCall(WRITE_MESSAGE_ROUTE, query_params, client, ReceiveWriteMessage);

}

public ReceiveWriteMessage(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);

    if(client && Successful)
    {
        PrintToChat(client, "[MapVotes] Comment Added");
    }

	Steam_ReleaseHTTPRequest(request);
}

public CastVote(client, value)
{

    if(!(value == -1 || value == 0 || value == 1)){
        LogError("[MapVotes] invalid vote value %d (steam_user: %s)", value, uid);
    }else{
        decl String:map[PLATFORM_MAX_PATH];
        decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
        GetClientAuthString(client, buffer, sizeof(buffer));
        GetCommunityIDString(buffer, uid, sizeof(uid));

        GetCurrentMap(map, sizeof(map));

        new HTTPRequestHandle:request = CreateMapVotesRequest(CAST_VOTE_ROUTE);
        Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
        Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "value", value);
        AddAccessCode(request);
        Steam_SendHTTPRequest(request, ReceiveCastVote, GetClientUserId(client));

        //MapVotesCall(CAST_VOTE_ROUTE, query_params, client, ReceiveCastVote);
    }

}

public ReceiveCastVote(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);

    if(client && Successful)
    {
        PrintToChat(client, "[MapVotes] Vote Cast");
    }

	Steam_ReleaseHTTPRequest(request);
}

public Favorite(String:map[PLATFORM_MAX_PATH], client, bool:favorite)
{
    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    GetClientAuthString(client, buffer, sizeof(buffer));
    GetCommunityIDString(buffer, uid, sizeof(uid));

    if(favorite)
    {
        //MapVotesCall(FAVORITE_ROUTE, query_params, client, ReceiveFavorite);
    }else{
        //MapVotesCall(UNFAVORITE_ROUTE, query_params, client, ReceiveFavorite);
    }
    new HTTPRequestHandle:request = CreateMapVotesRequest(FAVORITE_ROUTE);
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveFavorite, GetClientUserId(client));
}

public ReceiveFavorite(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);

    if(client && Successful)
    {
        PrintToChat(client, "[MapVotes] Updated Favorites");
    }

	Steam_ReleaseHTTPRequest(request);
}

public MapSearch(client, String:search_key[PLATFORM_MAX_PATH], Handle:map_list, MenuHandler:handler)
{
    new String:map[PLATFORM_MAX_PATH], String:info[16];
    new Handle:menu = CreateMenu(handler, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);
    new bool:found = false;

    for(new i=0; i<GetArraySize(map_list); i++)
    {
        GetArrayString(map_list, i, map, sizeof(map));

        //If this map matches the search key, add it to the menu
        if(StrContains(map, search_key, false) >= 0){
            AddMenuItem(menu, map, map);
            found = true;
        }
    }

    if(found)
    {
        SetMenuTitle(menu, "Found Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }

}

public FavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, true);
    }
}

public UnfavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, false);
    }
}

public GetFavorites(client)
{
    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    GetClientAuthString(client, buffer, sizeof(buffer));
    GetCommunityIDString(buffer, uid, sizeof(uid));
    decl String:query_params[512];

    //NOTE: uid is the client's steamid64 while player is the client's userid; the index incremented for each client that joined the server
    Format(query_params, sizeof(query_params),
            "player=%i&uid=%s", GetClientUserId(client), uid);

    //MapVotesCall(GET_FAVORITES_ROUTE, query_params, client, ReceiveGetFavorites);
    new HTTPRequestHandle:request = CreateMapVotesRequest(GET_FAVORITES_ROUTE);
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "player", GetClientUserId(client));
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveGetFavorites, GetClientUserId(client));
}

public ReceiveGetFavorites(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);
    if(client && Successful)
    {
        //TODO
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:maps = json_object_get(json, "maps");
    new String:map_buffer[PLATFORM_MAX_PATH];
    new junk;

    new Handle:menu = CreateMenu(NominateMapHandler);

    for(new i = 0; i < json_array_size(maps); i++)
    {
        json_array_get_string(maps, i, map_buffer, sizeof(map_buffer));
        if(GetTrieValue(g_MapTrie, map_buffer, junk))
        {
            AddMenuItem(menu, map_buffer, map_buffer);
        }
    }

    CloseHandle(json);
    CloseHandle(maps);

    //If no maps were found don't even bother displaying a menu
    if(GetMenuItemCount(menu) > 0){
        SetMenuTitle(menu, "Favorited Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }else{
        PrintToChat(client, "[MapVotes] You have no favorited maps that are on this server.");
    }
}

public NominateMapHandler(Handle:menu, MenuAction:action, param1, param2)
{

    decl result;

    // Start function call
    Call_StartFunction(g_nominations, g_Handler_MapSelectMenu);

    // Push parameters one at a time
    Call_PushCell(menu);
    Call_PushCell(action);
    Call_PushCell(param1);
    Call_PushCell(param2);

    // Finish the call, get the result
    Call_Finish(result);

    return result;
}

public CallVoteOnClient(client)
{
    new Handle:menu = CreateMenu(VoteMenuHandler);
    SetMenuTitle(menu, "Do you like this map?");
    AddMenuItem(menu, "1","Like it.");
    AddMenuItem(menu, "-1","Hate it.");
    AddMenuItem(menu, "0","I have no strong feelings one way or the other.");
    DisplayMenu(menu, client, 20);
}

public VoteMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new String:info[32];
        GetMenuItem(menu, param2, info, sizeof(info));
        new value = StringToInt(info);

        CastVote(param1, value);
    }
}

public HaveNotVoted()
{
    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    new String:query_buffer[512], String:query_params[512], String:map[PLATFORM_MAX_PATH];
    new player;
    new HTTPRequestHandle:request = CreateMapVotesRequest(HAVE_NOT_VOTED_ROUTE);

    GetCurrentMap(map, sizeof(map));
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);

    for (new client=1; client <= MaxClients; client++)
    {
        if (!IsClientInGame(client) || IsFakeClient(client))
        {
            continue;
        }
        GetClientAuthString(client, buffer, sizeof(buffer));
        GetCommunityIDString(buffer, uid, sizeof(uid));
        player = GetClientUserId(client);

        Steam_SetHTTPRequestGetOrPostParameter(request, "uids", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "players", player);
    }

    //MapVotesCall(HAVE_NOT_VOTED_ROUTE, query_params, 0, ReceiveHaveNotVoted);
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveHaveNotVoted, GetClientUserId(client));
}

public ReceiveHaveNotVoted(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);
    if(client && Successful)
    {
        //TODO
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:players = json_object_get(json, "players");
    new p;
    new String:map_buffer[PLATFORM_MAX_PATH];

    for(new i = 0; i < json_array_size(players); i++)
    {
        p = json_array_get_int(players, i);
        CallVoteOnClient(GetClientOfUserId(p));
    }
    CloseHandle(json);
    CloseHandle(players);
}

public ViewMap(client)
{
    decl String:map[PLATFORM_MAX_PATH], String:url[256], String:base_url[128];
    GetCurrentMap(map, sizeof(map));
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));
    //TODO
    ReplaceString(base_url, sizeof(base_url), "http://", "", false);
    ReplaceString(base_url, sizeof(base_url), "https://", "", false);

    Format(url, sizeof(url),
            "http://%s%s/%s", base_url, MAPS_ROUTE, map);

    ShowMOTDPanel(client, "Map Viewer", url, MOTDPANEL_TYPE_URL);

}

public Action:Test(client, args)
{
    decl String:query_params[512];

    //NOTE: uid is the client's steamid64 while player is the client's userid; the index incremented for each client that joined the server
    Format(query_params, sizeof(query_params),
            "player=%i&uid=%s", 7, "76561197998903004");

    //MapVotesCall(GET_FAVORITES_ROUTE, query_params, 0, ReceiveGetFavorites);
}

/**
 * vim: set ts=4 :
 * =============================================================================
 * map_votes
 * Interact with the Map Votes web api - https://github.com/CrimsonTautology/map_votes
 *
 * Copyright 2013 The Crimson Tautology
 * =============================================================================
 *
 */

#pragma semicolon 1

#include <sourcemod>
#include <steamtools>
#include <base64>
#include <smjansson>

#define PLUGIN_VERSION "0.1"

public Plugin:myinfo = {
    name = "MapVotes",
    author = "CrimsonTautology",
    description = "Interact with the Map Votes web api",
    version = PLUGIN_VERSION,
    url = "https://github.com/CrimsonTautology/sm_map_votes"
};

#define CAST_VOTE_ROUTE "/v1/api/cast_vote"
#define WRITE_MESSAGE_ROUTE "/v1/api/write_message"
#define FAVORITE_ROUTE "/v1/api/favorite"
#define UNFAVORITE_ROUTE "/v1/api/unfavorite"
#define GET_FAVORITES_ROUTE "/v1/api/get_favorites"
#define HAVE_NOT_VOTED_ROUTE "/v1/api/have_not_voted"
#define SERVER_QUERY_ROUTE "/v1/api/server_query"
#define MAPS_ROUTE "/maps"

#define MAX_STEAMID_LENGTH 21
#define MAX_COMMUNITYID_LENGTH 18

new Handle:g_Cvar_MapVotesUrl = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesPort = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesApiKey = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesVotingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesCommentingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesNominationsName = INVALID_HANDLE;

new g_MapFileSerial = -1;
new Handle:g_MapList = INVALID_HANDLE;
new Handle:g_MapTrie = INVALID_HANDLE;

new Handle:g_nominations = INVALID_HANDLE;
new Function:g_Handler_MapSelectMenu = INVALID_FUNCTION;

public OnPluginStart()
{

    g_Cvar_MapVotesUrl = CreateConVar("sm_map_votes_url", "", "URL to your Map Votes web page");
    g_Cvar_MapVotesPort = CreateConVar("sm_map_votes_port", "80", "HTTP Port used");
    g_Cvar_MapVotesApiKey = CreateConVar("sm_map_votes_api_key", "", "The API key you generated to interact with the Map Votes web page");
    g_Cvar_MapVotesVotingEnabled = CreateConVar("sm_map_votes_voting_enabled", "1", "Whether players are allowed to vote on the current map");
    g_Cvar_MapVotesCommentingEnabled = CreateConVar("sm_map_votes_commenting_enabled", "1", "Whether players are allowed to comment on the current map");
    g_Cvar_MapVotesNominationsName = CreateConVar("sm_map_votes_nominations_plugin", "nominations.smx", "The nominations plugin used by the server");

    RegConsoleCmd("sm_votemenu", Command_VoteMenu, "Bring up a menu to vote on the current map");
    RegConsoleCmd("sm_voteup", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_like", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_votedown", Command_VoteDown, "Vote that you hate the current map");
    RegConsoleCmd("sm_hate", Command_VoteDown, "Vote that you hate the current map");

    RegConsoleCmd("sm_fav", Command_Favorite, "Add this map to your favorites");
    RegConsoleCmd("sm_unfav", Command_Unfavorite, "Remove this map to your favorites");
    RegConsoleCmd("sm_nomfav", Command_GetFavorites, "Nominate from a list of your favorites");

    RegConsoleCmd("sm_mapcomment", Command_MapComment, "Comment on the current map");
    RegConsoleCmd("sm_mc", Command_MapComment, "Comment on the current map");

    RegConsoleCmd("sm_viewmap", Command_ViewMap, "View the Map Votes web page for this map");

    RegAdminCmd("sm_have_not_voted", Command_HaveNotVoted, ADMFLAG_VOTE, "Popup a vote panel to every player on the server that has not yet voted on this map");

    RegConsoleCmd("test", Test);

    new array_size = ByteCountToCells(PLATFORM_MAX_PATH);
    g_MapList = CreateArray(array_size);
    g_MapTrie = CreateTrie();
}

public OnConfigsExecuted()
{
    BuildMapListAndTrie();
}

public OnAllPluginsLoaded() {
    new String:noms[PLATFORM_MAX_PATH];
    GetConVarString(g_Cvar_MapVotesNominationsName, noms, sizeof(noms));
    //g_nominations = FindPluginByFile(noms);
    g_nominations = FindPluginByFile("nominations.smx");

    //Check if nominations.smx is both available and currently running
    if(g_nominations == INVALID_HANDLE || GetPluginStatus(g_nominations) != Plugin_Running){
        SetFailState("[MapVotes] Error, nominations is currently not running");
    }
    else{
        //We should be clear to link the MapSelectMenu function
        g_Handler_MapSelectMenu = GetFunctionByName(g_nominations, "Handler_MapSelectMenu");
    }
}

public Action:Command_VoteMenu(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        CallVoteOnClient(client);
    }

    return Plugin_Handled;
}

public Action:Command_VoteUp(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        CastVote(client, 1);
    }

    return Plugin_Handled;
}

public Action:Command_VoteDown(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        CastVote(client, -1);
    }

    return Plugin_Handled;
}

public Action:Command_Favorite(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, true);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, FavoriteSearchHandler);
        }
    }

    return Plugin_Handled;
}
public Action:Command_Unfavorite(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, false);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, UnfavoriteSearchHandler);
        }

    }

    return Plugin_Handled;
}
public Action:Command_GetFavorites(client, args)
{
    if(client && IsClientAuthorized(client) && GetConVarBool(g_Cvar_MapVotesVotingEnabled)){
        GetFavorites(client);
    }

    return Plugin_Handled;
}

public Action:Command_ViewMap(client, args)
{
    if(client && IsClientAuthorized(client)){
        ViewMap(client);
    }

    return Plugin_Handled;
}

public Action:Command_MapComment(client, args)
{
    if (!GetConVarBool(g_Cvar_MapVotesCommentingEnabled))
    {
        return Plugin_Handled;
    }

    if (args < 1)
    {
        ReplyToCommand(client, "[MapVotes] Usage: !map_comment <comment>");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        decl String:comment[256];
        GetCmdArgString(comment, sizeof(comment));
        WriteMessage(client, comment);
    }

    return Plugin_Handled;
}

public Action:Command_HaveNotVoted(client, args)
{
    HaveNotVoted();

    return Plugin_Handled;
}

BuildMapListAndTrie()
{
    //Build the map list
    if (ReadMapList(g_MapList,
                g_MapFileSerial,
                "nominations",
                MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER)
            == INVALID_HANDLE)
    {
        if (g_MapFileSerial == -1)
        {
            SetFailState("Unable to create a valid map list.");
        }else{
            //Build the map trie; note we don't care about the value, just if the map exists in the trie
            ClearTrie(g_MapTrie);
            new String:map[PLATFORM_MAX_PATH];

            for (new i = 0; i < GetArraySize(g_MapList); i++)
            {
                GetArrayString(g_MapList, i, map, sizeof(map));
                SetTrieValue(g_MapTrie, map, 1);
            }
        }
    }
}

//By 11530
//GetSteamAccountID(client) does not work because we don't have 64 bit types
//TODO - maybe deprecated
stock bool:GetCommunityIDString(const String:SteamID[], String:CommunityID[], const CommunityIDSize)
{
    decl String:SteamIDParts[3][11];
    new const String:Identifier[] = "76561197960265728";

    if ((CommunityIDSize < 1) || (ExplodeString(SteamID, ":", SteamIDParts, sizeof(SteamIDParts), sizeof(SteamIDParts[])) != 3))
    {
        CommunityID[0] = '\0';
        return false;
    }

    new Current, CarryOver = (SteamIDParts[1][0] == '1');
    for (new i = (CommunityIDSize - 2), j = (strlen(SteamIDParts[2]) - 1), k = (strlen(Identifier) - 1); i >= 0; i--, j--, k--)
    {
        Current = (j >= 0 ? (2 * (SteamIDParts[2][j] - '0')) : 0) + CarryOver + (k >= 0 ? ((Identifier[k] - '0') * 1) : 0);
        CarryOver = Current / 10;
        CommunityID[i] = (Current % 10) + '0';
    }

    CommunityID[CommunityIDSize - 1] = '\0';
    return true;
}

public AddAccessCode(&HTTPRequestHandle:request)
{
    decl String:api_key[128];
    GetConVarString(g_Cvar_MapVotesApiKey, api_key, sizeof(api_key));
	Steam_SetHTTPRequestGetOrPostParameter(request, "access_token", api_key);
}

public BuildAbsoluteURL(String:route[], String:out[])
{
    //TODO;
    decl String:base_url[128];
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));
    Format(out, sizeof(out),
            "%s%s", base_url, route);

    //ReplaceString(base_url, sizeof(base_url), "http://", "", false);
    //ReplaceString(base_url, sizeof(base_url), "https://", "", false);

}
public Steam_SetHTTPRequestGetOrPostParameterInt(&HTTPRequestHandle:request, const String:param[], value)
{
    String[64] tmp;
    IntToString(tmp, paramValue);
    Steam_SetHTTPRequestGetOrPostParameter(request, param, tmp);
}

public WriteMessage(client, String:message[256])
{
    //Encode the message to be url safe
    decl String:base64[256], String:base64_url[256];
    EncodeBase64(base64, sizeof(base64), message);
    Base64MimeToUrl(base64_url, sizeof(base64_url), base64);

    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    GetClientAuthString(client, buffer, sizeof(buffer));
    //TODO Steam_GetCSteamIDForClient?
    GetCommunityIDString(buffer, uid, sizeof(uid));

    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));

    new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
	Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
	Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
	Steam_SetHTTPRequestGetOrPostParameter(request, "comment", base64_url);
	Steam_SetHTTPRequestGetOrPostParameter(request, "base64", "1");
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveWriteMessage, GetClientUserId(client));

    //MapVotesCall(WRITE_MESSAGE_ROUTE, query_params, client, ReceiveWriteMessage);

}

public ReceiveWriteMessage(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);

    if(client && Successful)
    {
        PrintToChat(client, "[MapVotes] Comment Added");
    }

    //TODo
	//decl String:data[4096];
	//Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
	Steam_ReleaseHTTPRequest(request);

}

public CastVote(client, value)
{

    if(!(value == -1 || value == 0 || value == 1)){
        LogError("[MapVotes] invalid vote value %d (steam_user: %s)", value, uid);
    }else{
        decl String:map[PLATFORM_MAX_PATH];
        decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
        GetClientAuthString(client, buffer, sizeof(buffer));
        GetCommunityIDString(buffer, uid, sizeof(uid));

        GetCurrentMap(map, sizeof(map));

        new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
        Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
        Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "value", value);
        AddAccessCode(request);
        Steam_SendHTTPRequest(request, ReceiveCastVote, GetClientUserId(client));

        //MapVotesCall(CAST_VOTE_ROUTE, query_params, client, ReceiveCastVote);
    }

}

public ReceiveCastVote(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);

    if(client && Successful)
    {
        PrintToChat(client, "[MapVotes] Vote Cast");
    }

	Steam_ReleaseHTTPRequest(request);
}

public Favorite(String:map[PLATFORM_MAX_PATH], client, bool:favorite)
{
    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    GetClientAuthString(client, buffer, sizeof(buffer));
    GetCommunityIDString(buffer, uid, sizeof(uid));

    if(favorite)
    {
        //MapVotesCall(FAVORITE_ROUTE, query_params, client, ReceiveFavorite);
    }else{
        //MapVotesCall(UNFAVORITE_ROUTE, query_params, client, ReceiveFavorite);
    }
    new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveFavorite, GetClientUserId(client));
}

public ReceiveFavorite(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);

    if(client && Successful)
    {
        PrintToChat(client, "[MapVotes] Updated Favorites");
    }

	Steam_ReleaseHTTPRequest(request);
}

public MapSearch(client, String:search_key[PLATFORM_MAX_PATH], Handle:map_list, MenuHandler:handler)
{
    new String:map[PLATFORM_MAX_PATH], String:info[16];
    new Handle:menu = CreateMenu(handler, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);
    new bool:found = false;

    for(new i=0; i<GetArraySize(map_list); i++)
    {
        GetArrayString(map_list, i, map, sizeof(map));

        //If this map matches the search key, add it to the menu
        if(StrContains(map, search_key, false) >= 0){
            AddMenuItem(menu, map, map);
            found = true;
        }
    }

    if(found)
    {
        SetMenuTitle(menu, "Found Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }

}

public FavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, true);
    }
}

public UnfavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, false);
    }
}

public GetFavorites(client)
{
    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    GetClientAuthString(client, buffer, sizeof(buffer));
    GetCommunityIDString(buffer, uid, sizeof(uid));
    decl String:query_params[512];

    //NOTE: uid is the client's steamid64 while player is the client's userid; the index incremented for each client that joined the server
    Format(query_params, sizeof(query_params),
            "player=%i&uid=%s", GetClientUserId(client), uid);

    //MapVotesCall(GET_FAVORITES_ROUTE, query_params, client, ReceiveGetFavorites);
    new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "player", GetClientUserId(client));
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveGetFavorites, GetClientUserId(client));
}

public ReceiveGetFavorites(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);
    if(client && Successful)
    {
        //TODO
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:maps = json_object_get(json, "maps");
    new String:map_buffer[PLATFORM_MAX_PATH];
    new junk;

    new Handle:menu = CreateMenu(NominateMapHandler);

    for(new i = 0; i < json_array_size(maps); i++)
    {
        json_array_get_string(maps, i, map_buffer, sizeof(map_buffer));
        if(GetTrieValue(g_MapTrie, map_buffer, junk))
        {
            AddMenuItem(menu, map_buffer, map_buffer);
        }
    }

    CloseHandle(json);
    CloseHandle(maps);

    //If no maps were found don't even bother displaying a menu
    if(GetMenuItemCount(menu) > 0){
        SetMenuTitle(menu, "Favorited Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }else{
        PrintToChat(client, "[MapVotes] You have no favorited maps that are on this server.");
    }
}

public NominateMapHandler(Handle:menu, MenuAction:action, param1, param2)
{

    decl result;

    // Start function call
    Call_StartFunction(g_nominations, g_Handler_MapSelectMenu);

    // Push parameters one at a time
    Call_PushCell(menu);
    Call_PushCell(action);
    Call_PushCell(param1);
    Call_PushCell(param2);

    // Finish the call, get the result
    Call_Finish(result);

    return result;
}

public CallVoteOnClient(client)
{
    new Handle:menu = CreateMenu(VoteMenuHandler);
    SetMenuTitle(menu, "Do you like this map?");
    AddMenuItem(menu, "1","Like it.");
    AddMenuItem(menu, "-1","Hate it.");
    AddMenuItem(menu, "0","I have no strong feelings one way or the other.");
    DisplayMenu(menu, client, 20);
}

public VoteMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new String:info[32];
        GetMenuItem(menu, param2, info, sizeof(info));
        new value = StringToInt(info);

        CastVote(param1, value);
    }
}

public HaveNotVoted()
{
    decl String:buffer[MAX_STEAMID_LENGTH], String:uid[MAX_COMMUNITYID_LENGTH];
    new String:query_buffer[512], String:query_params[512], String:map[PLATFORM_MAX_PATH];
    new player;
    new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);

    GetCurrentMap(map, sizeof(map));
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);

    for (new client=1; client <= MaxClients; client++)
    {
        if (!IsClientInGame(client) || IsFakeClient(client))
        {
            continue;
        }
        GetClientAuthString(client, buffer, sizeof(buffer));
        GetCommunityIDString(buffer, uid, sizeof(uid));
        player = GetClientUserId(client);

        Steam_SetHTTPRequestGetOrPostParameter(request, "uids", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "players", player);
    }

    //MapVotesCall(HAVE_NOT_VOTED_ROUTE, query_params, 0, ReceiveHaveNotVoted);
    AddAccessCode(request);
    Steam_SendHTTPRequest(request, ReceiveHaveNotVoted, GetClientUserId(client));
}

public ReceiveHaveNotVoted(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid) {
{
    new client = GetClientOfUserId(userid);
    if(client && Successful)
    {
        //TODO
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:players = json_object_get(json, "players");
    new p;
    new String:map_buffer[PLATFORM_MAX_PATH];

    for(new i = 0; i < json_array_size(players); i++)
    {
        p = json_array_get_int(players, i);
        CallVoteOnClient(GetClientOfUserId(p));
    }
    CloseHandle(json);
    CloseHandle(players);
}

public ViewMap(client)
{
    decl String:map[PLATFORM_MAX_PATH], String:url[256], String:base_url[128];
    GetCurrentMap(map, sizeof(map));
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));
    //TODO
    ReplaceString(base_url, sizeof(base_url), "http://", "", false);
    ReplaceString(base_url, sizeof(base_url), "https://", "", false);

    Format(url, sizeof(url),
            "http://%s%s/%s", base_url, MAPS_ROUTE, map);

    ShowMOTDPanel(client, "Map Viewer", url, MOTDPANEL_TYPE_URL);

}

public Action:Test(client, args)
{
    decl String:query_params[512];

    //NOTE: uid is the client's steamid64 while player is the client's userid; the index incremented for each client that joined the server
    Format(query_params, sizeof(query_params),
            "player=%i&uid=%s", 7, "76561197998903004");

    //MapVotesCall(GET_FAVORITES_ROUTE, query_params, 0, ReceiveGetFavorites);
}