mangos/src/game/ChatCommands/GMTicketCommands.cpp
2020-12-30 16:54:00 +00:00

608 lines
17 KiB
C++

/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2021 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "Chat.h"
#include "Language.h"
#include "World.h"
#include "GMTicketMgr.h"
#include "Mail.h"
// show ticket (helper)
void ChatHandler::ShowTicket(GMTicket const* ticket)
{
std::string lastupdated = TimeToTimestampStr(ticket->GetLastUpdate());
std::string name;
if (!sObjectMgr.GetPlayerNameByGUID(ticket->GetPlayerGuid(), name))
{
name = GetMangosString(LANG_UNKNOWN);
}
std::string nameLink = playerLink(name);
char const* response = ticket->GetResponse();
PSendSysMessage(LANG_COMMAND_TICKETVIEW, nameLink.c_str(), lastupdated.c_str(), ticket->GetText());
if (strlen(response))
{
PSendSysMessage(LANG_COMMAND_TICKETRESPONSE, ticket->GetResponse());
}
}
// ticket commands
bool ChatHandler::HandleTicketAcceptCommand(char* args)
{
char* px = ExtractLiteralArg(&args);
// ticket<end>
if (!px)
{
return false;
}
// ticket accept on
if (strncmp(px, "on", 3) == 0)
{
sTicketMgr.SetAcceptTickets(true);
SendSysMessage(LANG_COMMAND_TICKETS_SYSTEM_ON);
}
// ticket accept off
else if (strncmp(px, "off", 4) == 0)
{
sTicketMgr.SetAcceptTickets(false);
SendSysMessage(LANG_COMMAND_TICKETS_SYSTEM_OFF);
}
else
{
return false;
}
return true;
}
bool ChatHandler::HandleTicketCloseCommand(char* args)
{
GMTicket* ticket = NULL;
uint32 num;
if (ExtractUInt32(&args, num))
{
if (num == 0)
{
return false;
}
ticket = sTicketMgr.GetGMTicket(num);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num);
SetSentErrorMessage(true);
return false;
}
}
else
{
ObjectGuid target_guid;
std::string target_name;
if (!ExtractPlayerTarget(&args, NULL, &target_guid, &target_name))
{
return false;
}
// ticket respond $char_name
ticket = sTicketMgr.GetGMTicket(target_guid);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST_NAME, target_name.c_str());
SetSentErrorMessage(true);
return false;
}
}
ObjectGuid target_guid = ticket->GetPlayerGuid();
// Get Player
// Can be nullptr if player is offline
Player* pPlayer = sObjectMgr.GetPlayer(target_guid);
// Get Player name
std::string target_name;
sObjectMgr.GetPlayerNameByGUID(target_guid, target_name);
if (!pPlayer && !sWorld.getConfig(CONFIG_BOOL_GM_TICKET_OFFLINE_CLOSING))
{
SendSysMessage(LANG_COMMAND_TICKET_CANT_CLOSE);
return false;
}
// Set reponse text if not existing
if (!*ticket->GetResponse())
{
const uint32 responseBufferSize = 256;
char response[responseBufferSize];
if (m_session)
{
const char* format = "[System Message] This ticket was closed by <GM>%s without any mail response, perhaps it was resolved by direct chat.";
const char* buffer;
snprintf(response, responseBufferSize, format, m_session->GetPlayer()->GetName());
}
else
{
strcpy(response, "[System Message] this ticket was closed using CLI console.");
}
ticket->SetResponseText(response);
}
ticket->Close();
// Define ticketId variable because we need ticket id after deleting it from TicketMgr
uint32 ticketId = ticket->GetId();
//This logic feels misplaced, but you can't have it in GMTicket?
// here, ticket become invalidated and should not be used below
sTicketMgr.Delete(ticket->GetPlayerGuid());
const char* gmNameReplacementWhenUsingCLI = "ADMIN";
// Send system Message to All Connected GMs to inform them the ticket has been closed
sObjectAccessor.DoForAllPlayers([&](Player* player)
{
if (player->GetSession()->GetSecurity() >= SEC_GAMEMASTER && player->isAcceptTickets())
{
ChatHandler(player).PSendSysMessage(LANG_COMMAND_TICKETCLOSED_NAME, ticketId, target_name.c_str(), m_session ? m_session->GetPlayer()->GetName() : gmNameReplacementWhenUsingCLI);
}
});
if (!m_session)
{
// In order to have message in CLI otherwise above code will only display to connected gms but not in console
PSendSysMessage(LANG_COMMAND_TICKETCLOSED_NAME, ticketId, target_name.c_str(), gmNameReplacementWhenUsingCLI);
}
return true;
}
// del tickets
bool ChatHandler::HandleTicketDeleteCommand(char* args)
{
char* px = ExtractLiteralArg(&args);
if (!px)
{
return false;
}
// ticket delete all
if (strncmp(px, "all", 4) == 0)
{
sTicketMgr.DeleteAll();
SendSysMessage(LANG_COMMAND_ALLTICKETDELETED);
return true;
}
uint32 num;
// ticket delete #num
if (ExtractUInt32(&px, num))
{
if (num == 0)
{
return false;
}
// mgr numbering tickets start from 0
GMTicket* ticket = sTicketMgr.GetGMTicket(num);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num);
SetSentErrorMessage(true);
return false;
}
ObjectGuid guid = ticket->GetPlayerGuid();
sTicketMgr.Delete(guid);
// notify player
if (Player* pl = sObjectMgr.GetPlayer(guid))
{
pl->GetSession()->SendGMTicketGetTicket(0x0A);
PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL, GetNameLink(pl).c_str());
}
else
{
PSendSysMessage(LANG_COMMAND_TICKETDEL);
}
return true;
}
// ticket delete $charName
Player* target;
ObjectGuid target_guid;
std::string target_name;
if (!ExtractPlayerTarget(&px, &target, &target_guid, &target_name))
{
return false;
}
// ticket delete $charName
sTicketMgr.Delete(target_guid);
// notify players about ticket deleting
if (target)
{
target->GetSession()->SendGMTicketGetTicket(0x0A);
}
std::string nameLink = playerLink(target_name);
PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL, nameLink.c_str());
return true;
}
bool ChatHandler::HandleTicketInfoCommand(char* args)
{
size_t count = sTicketMgr.GetTicketCount();
if (m_session)
{
PSendSysMessage(LANG_COMMAND_TICKETCOUNT, count, GetOnOffStr(m_session->GetPlayer()->isAcceptTickets()));
}
else
{
PSendSysMessage(LANG_COMMAND_TICKETCOUNT_CONSOLE, count);
}
return true;
}
bool ChatHandler::HandleTicketListCommand(char* args)
{
uint16 numToShow = std::min(uint16(sTicketMgr.GetTicketCount()), uint16(sWorld.getConfig(CONFIG_UINT32_GM_TICKET_LIST_SIZE)));
for (uint16 i = 0; i < numToShow; ++i)
{
GMTicket* ticket = sTicketMgr.GetGMTicketByOrderPos(i);
time_t lastChanged = time_t(ticket->GetLastUpdate());
PSendSysMessage(LANG_COMMAND_TICKET_OFFLINE_INFO, ticket->GetId(), ticket->GetPlayerGuid().GetCounter(), ticket->HasResponse() ? "+" : "-", ctime(&lastChanged));
}
PSendSysMessage(LANG_COMMAND_TICKET_COUNT_ALL, numToShow, sTicketMgr.GetTicketCount());
return true;
}
bool ChatHandler::HandleTicketOnlineListCommand(char* args)
{
uint16 count = 0;
for (uint16 i = 0; i < sTicketMgr.GetTicketCount(); ++i)
{
GMTicket* ticket = sTicketMgr.GetGMTicketByOrderPos(i);
if (Player* player = sObjectMgr.GetPlayer(ticket->GetPlayerGuid(), true))
{
++count;
if (i < sWorld.getConfig(CONFIG_UINT32_GM_TICKET_LIST_SIZE))
{
time_t lastChanged = time_t(ticket->GetLastUpdate());
PSendSysMessage(LANG_COMMAND_TICKET_BRIEF_INFO, ticket->GetId(), player->GetName(), ticket->HasResponse() ? "+" : "-", ctime(&lastChanged));
}
}
}
PSendSysMessage(LANG_COMMAND_TICKET_COUNT_ONLINE, std::min(count, uint16(sWorld.getConfig(CONFIG_UINT32_GM_TICKET_LIST_SIZE))), count);
return true;
}
bool ChatHandler::HandleTicketMeAcceptCommand(char* args)
{
char* px = ExtractLiteralArg(&args);
if (!px)
{
PSendSysMessage(LANG_COMMAND_TICKET_ACCEPT_STATE, m_session->GetPlayer()->isAcceptTickets() ? "on" : "off");
return true;
}
if (!m_session)
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
SetSentErrorMessage(true);
return false;
}
// ticket on
if (strncmp(px, "on", 3) == 0)
{
m_session->GetPlayer()->SetAcceptTicket(true);
SendSysMessage(LANG_COMMAND_TICKETON);
}
// ticket off
else if (strncmp(px, "off", 4) == 0)
{
m_session->GetPlayer()->SetAcceptTicket(false);
SendSysMessage(LANG_COMMAND_TICKETOFF);
}
else
{
return false;
}
return true;
}
bool ChatHandler::HandleTicketRespondCommand(char* args)
{
GMTicket* ticket = NULL;
// ticket respond #num
uint32 num;
if (ExtractUInt32(&args, num))
{
if (num == 0)
{
return false;
}
// mgr numbering tickets start from 0
ticket = sTicketMgr.GetGMTicket(num);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num);
SetSentErrorMessage(true);
return false;
}
}
else
{
ObjectGuid target_guid;
std::string target_name;
if (!ExtractPlayerTarget(&args, NULL, &target_guid, &target_name))
{
return false;
}
// ticket respond $char_name
ticket = sTicketMgr.GetGMTicket(target_guid);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST_NAME, target_name.c_str());
SetSentErrorMessage(true);
return false;
}
}
// no response text?
if (!*args)
{
return false;
}
// Set the response text to the ticket
ticket->SetResponseText(args);
// Send in-game email with ticket answer
MailDraft draft;
const char* signatureFormat = GetMangosString(LANG_COMMAND_TICKET_RESPOND_MAIL_SIGNATURE);
const uint32 signatureBufferSize = 256;
char signature[signatureBufferSize];
if (m_session)
{
snprintf(signature, signatureBufferSize, signatureFormat, m_session->GetPlayer()->GetName());
}
else
{
// Used when the command is used via CLI console
strcpy(signature, "$B$BBest regards, $B$BThe Server Admin");
}
std::string mailText = args;
mailText = mailText + signature;
draft.SetSubjectAndBody(GetMangosString(LANG_COMMAND_TICKET_RESPOND_MAIL_SUBJECT), mailText);
uint32 senderGuidLow = 0;
if (m_session)
{
senderGuidLow = m_session->GetPlayer()->GetGUIDLow();
}
MailSender sender(MAIL_NORMAL, senderGuidLow, MAIL_STATIONERY_GM);
ObjectGuid target_guid = ticket->GetPlayerGuid();
// Get Player
// Can be nullptr if player is offline
Player* target = sObjectMgr.GetPlayer(target_guid);
// Get Player name
std::string target_name;
sObjectMgr.GetPlayerNameByGUID(target_guid, target_name);
// Find player to send, hopefully we have his guid if target is nullpt
// Todo set MailDraft sent by GM and handle 90 day delay
draft.SendMailTo(MailReceiver(target, target_guid), sender);
const char* gmNameReplacementWhenUsingCLI = "ADMIN";
// If player is online, notify with a system message that the ticket was handled.
if (target && target->IsInWorld())
{
ChatHandler(target).PSendSysMessageMultiline(LANG_COMMAND_TICKETCLOSED_PLAYER_NOTIF, m_session ? m_session->GetPlayer()->GetName() : gmNameReplacementWhenUsingCLI);
}
// Define ticketId variable because we need ticket id after deleting it from TicketMgr in notification formated string
uint32 ticketId = ticket->GetId();
// Close the ticket
ticket->Close();
// Remove ticket from ticket manager
// Otherwise ticket will reappear in player UI if teleported or logout/login !
sTicketMgr.Delete(ticket->GetPlayerGuid());
// Send system Message to All Connected GMs to informe them the ticket has been closed
sObjectAccessor.DoForAllPlayers([&](Player* player)
{
if (player->GetSession()->GetSecurity() >= SEC_GAMEMASTER && player->isAcceptTickets())
{
ChatHandler(player).PSendSysMessage(LANG_COMMAND_TICKETCLOSED_NAME, ticketId, target_name.c_str(), m_session ? m_session->GetPlayer()->GetName() : gmNameReplacementWhenUsingCLI);
}
});
if (!m_session)
{
// In order to have message in CLI otherwise above code will only display to connected gms but not in console
PSendSysMessage(LANG_COMMAND_TICKETCLOSED_NAME, ticketId, target_name.c_str(), gmNameReplacementWhenUsingCLI);
}
return true;
}
bool ChatHandler::HandleTicketShowCommand(char* args)
{
// ticket #num
char* px = ExtractLiteralArg(&args);
if (!px)
{
return false;
}
uint32 num;
if (ExtractUInt32(&px, num))
{
if (num == 0)
{
return false;
}
// mgr numbering tickets start from 0
GMTicket* ticket = sTicketMgr.GetGMTicket(num);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num);
SetSentErrorMessage(true);
return false;
}
ShowTicket(ticket);
return true;
}
ObjectGuid target_guid;
std::string target_name;
if (!ExtractPlayerTarget(&px, NULL, &target_guid, &target_name))
{
return false;
}
// ticket $char_name
GMTicket* ticket = sTicketMgr.GetGMTicket(target_guid);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST_NAME, target_name.c_str());
SetSentErrorMessage(true);
return false;
}
ShowTicket(ticket);
return true;
}
bool ChatHandler::HandleTickerSurveyClose(char* args)
{
GMTicket* ticket = NULL;
std::string target_name;
ObjectGuid target_guid;
uint32 num;
if (ExtractUInt32(&args, num))
{
if (num == 0)
{
return false;
}
ticket = sTicketMgr.GetGMTicket(num);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num);
SetSentErrorMessage(true);
return false;
}
}
else
{
if (!ExtractPlayerTarget(&args, NULL, &target_guid, &target_name))
{
return false;
}
// ticket respond $char_name
ticket = sTicketMgr.GetGMTicket(target_guid);
if (!ticket)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST_NAME, target_name.c_str());
SetSentErrorMessage(true);
return false;
}
}
uint32 ticketId = ticket->GetId();
ticket->CloseWithSurvey();
//This needs to be before we delete the ticket
Player* pPlayer = sObjectMgr.GetPlayer(ticket->GetPlayerGuid());
//For now we can't close tickets for offline players, TODO
if (!pPlayer)
{
SendSysMessage(LANG_COMMAND_TICKET_CANT_CLOSE);
return false;
}
//This logic feels misplaced, but you can't have it in GMTicket?
sTicketMgr.Delete(ticket->GetPlayerGuid());
ticket = NULL;
const char* gmNameReplacementWhenUsingCLI = "ADMIN";
PSendSysMessage(LANG_COMMAND_TICKETCLOSED_NAME, ticketId, target_name.c_str(), m_session ? m_session->GetPlayer()->GetName() : gmNameReplacementWhenUsingCLI);
return true;
}