#include "IRCClient.h" #include "IRCCmd.h" #include "IRCFunc.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "WorldPacket.h" #include "ChannelMgr.h" #include "Config/ConfigEnv.h" #include "Channel.h" #include "World.h" IRCCmd Command; void IRCClient::Handle_IRC(std::string sData) { sLog.outDebug(sData.c_str()); // If first 5 chars are ERROR then something is wrong // either link is being closed, nickserv ghost command, etc... if(sData.substr(0, 5) == "ERROR") { Disconnect(); return; } if(sData.substr(0, 4) == "PING") { // if the first 4 characters contain PING // the server is checking if we are still alive // sen back PONG back plus whatever the server send with it SendIRC("PONG " + sData.substr(4, sData.size() - 4)); } else { // if the first line contains : its an irc message // such as private messages channel join etc. if(sData.substr(0, 1) == ":") { // find the spaces in the receieved line size_t p1 = sData.find(" "); size_t p2 = sData.find(" ", p1 + 1); // because the irc protocol uses simple spaces // to seperate data we can easy pick them out // since we know the position of the spaces std::string USR = sData.substr(1, p1 - 1); std::string CMD = sData.substr(p1 + 1, p2 - p1 - 1); // trasform the commands to lowercase to make sure they always match std::transform(CMD.begin(), CMD.end(), CMD.begin(), towlower); // Extract the username from the first part std::string szUser = GetUser(USR); // if we receieved the internet connect code // we know for sure that were in and we can // authenticate ourself. if(CMD == sIRC._ICC) { // _Auth is defined in mangosd.conf (irc.auth) // 0 do not authenticate // 1 use nickserv // 2 use quakenet // aditionally you can provide you own authentication method here switch(sIRC._Auth) { case 1: SendIRC("PRIVMSG nickserv :IDENTIFY " + sIRC._Pass); break; case 2: SendIRC("PRIVMSG nickserv :IDENTIFY " + sIRC._Auth_Nick + " " + sIRC._Pass); break; case 3: SendIRC("PRIVMSG Q@CServe.quakenet.org :AUTH " + sIRC._Nick + " " + sIRC._Pass); break; case 4: SendIRC("PRIVMSG Q@CServe.quakenet.org :AUTH " + sIRC._Auth_Nick + " " + sIRC._Pass); break; } // if we join a default channel leave this now. if(sIRC._ldefc==1) SendIRC("PART #" + sIRC._defchan); // Loop thru the channel array and send a command to join them on IRC. for(int i=1;i < sIRC._chan_count + 1;i++) { SendIRC("JOIN #" + sIRC._irc_chan[i]); } } // someone joined the channel this could be the bot or another user if(CMD == "join") { size_t p = sData.find(":", p1); std::string CHAN = sData.substr(p + 1, sData.size() - p - 2); // if the user is us it means we join the channel if ((szUser == sIRC._Nick) ) { // its us that joined the channel Send_IRC_Channel(CHAN, MakeMsg(MakeMsg(sIRC.JoinMsg, "$Ver", sIRC._Mver.c_str()), "$Trigger", sIRC._cmd_prefx.c_str()), true); } else { // if the user is not us its someone else that joins // so we construct a message and send this to the clients. // MangChat now uses Send_WoW_Channel to send to the client // this makes MangChat handle the packets instead of previously the world. if((sIRC.BOTMASK & 2) != 0) Send_WoW_Channel(GetWoWChannel(CHAN).c_str(), IRCcol2WoW(MakeMsg(MakeMsg(GetChatLine(JOIN_IRC), "$Name", szUser), "$Channel", GetWoWChannel(CHAN)))); } } // someone on irc left or quit the channel if(CMD == "part" || CMD == "quit") { size_t p3 = sData.find(" ", p2 + 1); std::string CHAN = sData.substr(p2 + 1, p3 - p2 - 1); // Logout IRC Nick From MangChat If User Leaves Or Quits IRC. if(Command.IsLoggedIn(szUser)) { _CDATA CDATA; CDATA.USER = szUser; Command.Handle_Logout(&CDATA); } // Construct a message and inform the clients on the same channel. if((sIRC.BOTMASK & 2) != 0) Send_WoW_Channel(GetWoWChannel(CHAN).c_str(), IRCcol2WoW(MakeMsg(MakeMsg(GetChatLine(LEAVE_IRC), "$Name", szUser), "$Channel", GetWoWChannel(CHAN)))); } // someone changed their nick if (CMD == "nick" && (sIRC.BOTMASK & 128) != 0) { MakeMsg(MakeMsg(GetChatLine(CHANGE_NICK), "$Name", szUser), "$NewName", sData.substr(sData.find(":", p2) + 1)); // If the user is logged in and changes their nick // then we want to either log them out or update // their nick in the bot. I chose to update the bots user list. if(Command.IsLoggedIn(szUser)) { std::string NewNick = sData.substr(sData.find(":", p2) + 1); // On freenode I noticed the server sends an extra character // at the end of the string, so we need to erase the last // character of the string. if you have a problem with getting // the last letter of your nick erased, then remove the - 1. NewNick.erase(NewNick.length() - 1, 1); for(std::list<_client*>::iterator i=Command._CLIENTS.begin(); i!=Command._CLIENTS.end();i++) { if((*i)->Name == szUser) { (*i)->Name = NewNick; sIRC.Send_IRC_Channel(NewNick.c_str(), "I Noticed You Changed Your Nick, I Have Updated My Internal Database Accordingly.", true, "NOTICE"); // Figure why not output to the logfile, makes tracing problems easier. sIRC.iLog.WriteLog(" %s : %s Changed Nick To: %s", sIRC.iLog.GetLogDateTimeStr().c_str(), szUser.c_str(), NewNick.c_str()); } } } } // someone was kicked from irc if (CMD == "kick") { // extract the details size_t p3 = sData.find(" ", p2 + 1); size_t p4 = sData.find(" ", p3 + 1); size_t p5 = sData.find(":", p4); std::string CHAN = sData.substr(p2 + 1, p3 - p2 - 1); std::string WHO = sData.substr(p3 + 1, p4 - p3 - 1); std::string BY = sData.substr(p4 + 1, sData.size() - p4 - 1); // if the one kicked was us if(WHO == sIRC._Nick) { // and autojoin is enabled // return to the channel if(sIRC._autojoinkick == 1) { SendIRC("JOIN " + CHAN); Send_IRC_Channel(CHAN, sIRC.kikmsg, true); } } else { // if it is not us who was kicked we need to inform the clients someone // was removed from the channel // construct a message and send it to the players. Send_WoW_Channel(GetWoWChannel(CHAN).c_str(), "[" + WHO + "]: Was Kicked From " + CHAN + " By: " + szUser); } } // a private chat message was receieved. if(CMD == "privmsg" || CMD == "notice") { // extract the values size_t p = sData.find(" ", p2 + 1); std::string FROM = sData.substr(p2 + 1, p - p2 - 1); std::string CHAT = sData.substr(p + 2, sData.size() - p - 3); // if this is our username it means we recieved a PM if(FROM == sIRC._Nick) { if(CHAT.find("\001VERSION\001") < CHAT.size()) { Send_IRC_Channel(szUser, MakeMsg("\001VERSION MangChat %s ©2008 |Death|\001", "%s" , sIRC._Mver.c_str()), true, "PRIVMSG"); } // a pm is required for certain commands // such as login. to validate the command // we send it to the command class wich handles // evrything else. Command.IsValid(szUser, FROM, CHAT, CMD); } else { // if our name is not in it, it means we receieved chat on one of the channels // magchat is in. the first thing we do is check if it is a command or not if(!Command.IsValid(szUser, FROM, CHAT, CMD)) { Send_WoW_Channel(GetWoWChannel(FROM).c_str(), IRCcol2WoW(MakeMsg(MakeMsg(GetChatLine(IRC_WOW), "$Name", szUser), "$Msg", CHAT))); } // if we indeed receieved a command we do not want to display this to the players // so only incanse the isvalid command returns false it will be sent to all player. // the isvalid function will automaitcly process the command on true. } } if(CMD == "mode") { // extract the mode details size_t p3 = sData.find(" ", p2 + 1); size_t p4 = sData.find(" ", p3 + 1); size_t p5 = sData.find(" ", p4 + 1); std::string CHAN = sData.substr(p2 + 1, p3 - p2 - 1); std::string MODE = sData.substr(p3 + 1, p4 - p3 - 1); std::string NICK = sData.substr(p4 + 1, p5 - p4 - 1); bool _AmiOp; _AmiOp = false; //A mode was changed on us if(NICK.c_str() == sIRC._Nick) _AmiOp = true; } } } } // This function is called in Channel.h // based on nAction it will inform the people on // irc when someone leaves one of the game channels. // nAction is based on the struct CACTION void IRCClient::Handle_WoW_Channel(std::string Channel, Player *plr, int nAction) { // make sure that we are connected if(sIRC.Connected && (sIRC.BOTMASK & 1)!= 0) { if(Channel_Valid(Channel)) { std::string GMRank = ""; std::string pname = plr->GetName(); bool DoGMAnnounce = false; if (plr->GetSession()->GetSecurity() > 0 && (sIRC.BOTMASK & 8)!= 0) DoGMAnnounce = true; if (plr->isGameMaster() && (sIRC.BOTMASK & 16)!= 0) DoGMAnnounce = true; if(DoGMAnnounce) { switch(plr->GetSession()->GetSecurity()) //switch case to determine what rank the "gm" is { case 0: GMRank = "";break; case 1: GMRank = "\0037"+sIRC.ojGM1;break; case 2: GMRank = "\0037"+sIRC.ojGM2;break; case 3: GMRank = "\0037"+sIRC.ojGM3;break; case 4: GMRank = "\0037"+sIRC.ojGM4;break; case 5: GMRank = "\0037"+sIRC.ojGM5;break; } } std::string ChatTag = ""; switch (plr->GetTeam()) { case 67:ChatTag.append("\0034");break; //horde case 469:ChatTag.append("\00312");break; //alliance } std::string query = "INSERT INTO `IRC_Inchan` VALUES (%d,'"+pname+"','"+Channel+"')"; std::string lchan = "DELETE FROM `IRC_Inchan` WHERE `guid` = %d AND `channel` = '"+Channel+"'"; switch(nAction) { case CHANNEL_JOIN: Send_IRC_Channel(GetIRCChannel(Channel), MakeMsg(MakeMsg(MakeMsg(GetChatLine(JOIN_WOW), "$Name", ChatTag + plr->GetName()), "$Channel", Channel), "$GM", GMRank)); WorldDatabase.PExecute(lchan.c_str(), plr->GetGUID()); WorldDatabase.PExecute(query.c_str(), plr->GetGUID()); break; case CHANNEL_LEAVE: Send_IRC_Channel(GetIRCChannel(Channel), MakeMsg(MakeMsg(MakeMsg(GetChatLine(LEAVE_WOW), "$Name", ChatTag + plr->GetName()), "$Channel", Channel), "$GM", GMRank)); WorldDatabase.PExecute(lchan.c_str(), plr->GetGUID()); break; } } } } // This function sends chat to a irc channel or user // to prevent the # beeing appended to send a msg to a user // set the NoPrefix to true void IRCClient::Send_IRC_Channel(std::string sChannel, std::string sMsg, bool NoPrefix, std::string nType) { std::string mType = "PRIVMSG"; if(Command.MakeUpper(nType.c_str()) == "NOTICE") mType = "NOTICE"; if(Command.MakeUpper(nType.c_str()) == "ERROR" && (sIRC.BOTMASK & 32)!= 0) mType = "NOTICE"; if(sIRC.Connected) { if(NoPrefix) SendIRC(mType + " " + sChannel + " :" + sMsg); else SendIRC(mType + " #" + sChannel + " :" + sMsg); } } // This function sends a message to all irc channels // that mangchat has in its configuration void IRCClient::Send_IRC_Channels(std::string sMsg) { for(int i=1;i < sIRC._chan_count + 1;i++) Send_IRC_Channel(sIRC._irc_chan[i], sMsg); } // This function is called in ChatHandler.cpp, any channel chat from wow will come // to this function, validates the channel and constructs a message that is send to IRC void IRCClient::Send_WoW_IRC(Player *plr, std::string Channel, std::string Msg) { // Check if the channel exist in our configuration if(Channel_Valid(Channel) && Msg.substr(0, 1) != ".") Send_IRC_Channel(GetIRCChannel(Channel), MakeMsgP(WOW_IRC, Msg, plr)); } void IRCClient::Send_WoW_Player(std::string sPlayer, std::string sMsg) { normalizePlayerName(sPlayer); if (Player* plr = ObjectAccessor::Instance().FindPlayerByName(sPlayer.c_str())) Send_WoW_Player(plr, sMsg); } void IRCClient::Send_WoW_Player(Player *plr, string sMsg) { WorldPacket data(SMSG_MESSAGECHAT, 200); data << (uint8)CHAT_MSG_SYSTEM; data << (uint32)LANG_UNIVERSAL; data << (uint64)plr->GetGUID(); data << (uint32)0; data << (uint64)plr->GetGUID(); data << (uint32)(sMsg.length()+1); data << sMsg; data << (uint8)0; plr->GetSession()->SendPacket(&data); } // This function will construct and send a packet to all players // on the given channel ingame. (previuosly found in world.cpp) // it loops thru all sessions and checks if they are on the channel // if so construct a packet and send it. void IRCClient::Send_WoW_Channel(const char *channel, std::string chat) { if(!(strlen(channel) > 0)) return; #ifdef USE_UTF8 std::string chat2 = chat; if(ConvertUTF8(chat2.c_str(), chat2)) chat = chat2; #endif HashMapHolder::MapType& m = ObjectAccessor::Instance().GetPlayers(); for(HashMapHolder::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) { if (itr->second && itr->second->GetSession()->GetPlayer() && itr->second->GetSession()->GetPlayer()->IsInWorld()) { if(ChannelMgr* cMgr = channelMgr(itr->second->GetSession()->GetPlayer()->GetTeam())) { if(Channel *chn = cMgr->GetChannel(channel, itr->second->GetSession()->GetPlayer())) { WorldPacket data; data.Initialize(SMSG_MESSAGECHAT); data << (uint8)CHAT_MSG_CHANNEL; data << (uint32)LANG_UNIVERSAL; data << (uint64)0; data << (uint32)0; data << channel; data << (uint64)0; data << (uint32) (strlen(chat.c_str()) + 1); data << IRCcol2WoW(chat.c_str()); data << (uint8)0; itr->second->GetSession()->SendPacket(&data); } } } } } void IRCClient::Send_WoW_System(std::string Message) { HashMapHolder::MapType& m = ObjectAccessor::Instance().GetPlayers(); for(HashMapHolder::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) { if (itr->second && itr->second->GetSession()->GetPlayer() && itr->second->GetSession()->GetPlayer()->IsInWorld()) { WorldPacket data; data.Initialize(CHAT_MSG_SYSTEM); data << (uint8)CHAT_MSG_SYSTEM; data << (uint32)LANG_UNIVERSAL; data << (uint64)0; data << (uint32)0; data << (uint64)0; data << (uint32) (strlen(Message.c_str()) + 1); data << Message.c_str(); data << (uint8)0; itr->second->GetSession()->SendPacket(&data); } } } void IRCClient::ResetIRC() { SendData("QUIT"); Disconnect(); } #define CHAT_INVITE_NOTICE 0x18 // this function should be called on player login Player::AddToWorld void IRCClient::AutoJoinChannel(Player *plr) { //this will work if at least 1 player is logged in regrdless if he is on the channel or not // the first person that login empty server is the one with bad luck and wont be invited, // if at least 1 player is online the player will be inited to the chanel std::string m_name = sIRC.ajchan; WorldPacket data; data.Initialize(SMSG_CHANNEL_NOTIFY, 1+m_name.size()+1); data << uint8(CHAT_INVITE_NOTICE); data << m_name.c_str(); HashMapHolder::MapType& m = ObjectAccessor::Instance().GetPlayers(); for(HashMapHolder::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) { if (itr->second && itr->second->GetSession()->GetPlayer() && itr->second->GetSession()->GetPlayer()->IsInWorld()) { data << uint64(itr->second->GetGUID()); break; } } plr->GetSession()->SendPacket(&data); }