root/trunk/src/game/IRCIO.cpp @ 40

Revision 39, 18.7 kB (checked in by yumileroy, 17 years ago)

[svn] * Various small changes here and there.
* Implementing MangChat? IRC system.
* Added new config option, MAX_WHO, can be used to set the limit of characters being sent in a /who request from client.

Original author: XTZGZoReX
Date: 2008-10-12 14:03:38-05:00

Line 
1#include "IRCClient.h"
2#include "IRCCmd.h"
3#include "IRCFunc.h"
4#include "ObjectAccessor.h"
5#include "ObjectMgr.h"
6#include "WorldPacket.h"
7#include "ChannelMgr.h"
8#include "Config/ConfigEnv.h"
9#include "Channel.h"
10#include "World.h"
11
12IRCCmd Command;
13void IRCClient::Handle_IRC(std::string sData)
14{
15    sLog.outDebug(sData.c_str());
16    // If first 5 chars are ERROR then something is wrong
17    // either link is being closed, nickserv ghost command, etc...
18    if(sData.substr(0, 5) == "ERROR")
19    {
20        Disconnect();
21        return;
22    }
23    if(sData.substr(0, 4) == "PING")
24    {
25        // if the first 4 characters contain PING
26        // the server is checking if we are still alive
27        // sen back PONG back plus whatever the server send with it
28        SendIRC("PONG " + sData.substr(4, sData.size() - 4));
29    }
30    else
31    {
32        // if the first line contains : its an irc message
33        // such as private messages channel join etc.
34        if(sData.substr(0, 1) == ":")
35        {
36            // find the spaces in the receieved line
37            size_t p1 = sData.find(" ");
38            size_t p2 = sData.find(" ", p1 + 1);
39            // because the irc protocol uses simple spaces
40            // to seperate data we can easy pick them out
41            // since we know the position of the spaces
42            std::string USR = sData.substr(1, p1 - 1);
43            std::string CMD = sData.substr(p1 + 1, p2 - p1 - 1);
44            // trasform the commands to lowercase to make sure they always match
45            std::transform(CMD.begin(), CMD.end(), CMD.begin(), towlower);
46            // Extract the username from the first part
47            std::string szUser = GetUser(USR);
48            // if we receieved the internet connect code
49            // we know for sure that were in and we can
50            // authenticate ourself.
51            if(CMD == sIRC._ICC)
52            {
53                // _Auth is defined in mangosd.conf (irc.auth)
54                // 0 do not authenticate
55                // 1 use nickserv
56                // 2 use quakenet
57                // aditionally you can provide you own authentication method here
58                switch(sIRC._Auth)
59                {
60                    case 1:
61                        SendIRC("PRIVMSG nickserv :IDENTIFY " + sIRC._Pass);
62                        break;
63                    case 2:
64                        SendIRC("PRIVMSG nickserv :IDENTIFY " + sIRC._Auth_Nick + " " + sIRC._Pass);
65                        break;
66                    case 3:
67                        SendIRC("PRIVMSG Q@CServe.quakenet.org :AUTH " + sIRC._Nick + " " + sIRC._Pass);
68                        break;
69                                        case 4:
70                        SendIRC("PRIVMSG Q@CServe.quakenet.org :AUTH " + sIRC._Auth_Nick + " " + sIRC._Pass);
71                        break;
72                                }
73                // if we join a default channel leave this now.
74                if(sIRC._ldefc==1)
75                    SendIRC("PART #" + sIRC._defchan);
76                // Loop thru the channel array and send a command to join them on IRC.
77                for(int i=1;i < sIRC._chan_count + 1;i++)
78                {
79                    SendIRC("JOIN #" + sIRC._irc_chan[i]);
80                }
81            }
82            // someone joined the channel this could be the bot or another user
83            if(CMD == "join")
84            {
85                size_t p = sData.find(":", p1);
86                std::string CHAN = sData.substr(p + 1, sData.size() - p - 2);
87                // if the user is us it means we join the channel
88                if ((szUser == sIRC._Nick) )
89                {
90                    // its us that joined the channel
91                    Send_IRC_Channel(CHAN, MakeMsg(MakeMsg(sIRC.JoinMsg, "$Ver", sIRC._Mver.c_str()), "$Trigger", sIRC._cmd_prefx.c_str()), true);
92                }
93                else
94                {
95                    // if the user is not us its someone else that joins
96                    // so we construct a message and send this to the clients.
97                    // MangChat now uses Send_WoW_Channel to send to the client
98                    // this makes MangChat handle the packets instead of previously the world.
99                    if((sIRC.BOTMASK & 2) != 0)
100                        Send_WoW_Channel(GetWoWChannel(CHAN).c_str(), IRCcol2WoW(MakeMsg(MakeMsg(GetChatLine(JOIN_IRC), "$Name", szUser), "$Channel", GetWoWChannel(CHAN))));
101                }
102            }
103            // someone on irc left or quit the channel
104            if(CMD == "part" || CMD == "quit")
105            {
106                size_t p3 = sData.find(" ", p2 + 1);
107                std::string CHAN = sData.substr(p2 + 1, p3 - p2 - 1);
108                // Logout IRC Nick From MangChat If User Leaves Or Quits IRC.
109                if(Command.IsLoggedIn(szUser))
110                {
111                    _CDATA CDATA;
112                    CDATA.USER      = szUser;
113                    Command.Handle_Logout(&CDATA);
114                }
115                // Construct a message and inform the clients on the same channel.
116                                if((sIRC.BOTMASK & 2) != 0)
117                    Send_WoW_Channel(GetWoWChannel(CHAN).c_str(), IRCcol2WoW(MakeMsg(MakeMsg(GetChatLine(LEAVE_IRC), "$Name", szUser), "$Channel", GetWoWChannel(CHAN))));     
118            }
119            // someone changed their nick
120                        if (CMD == "nick" && (sIRC.BOTMASK & 128) != 0)
121            {
122                MakeMsg(MakeMsg(GetChatLine(CHANGE_NICK), "$Name", szUser), "$NewName", sData.substr(sData.find(":", p2) + 1));
123                                // If the user is logged in and changes their nick
124                                // then we want to either log them out or update
125                                // their nick in the bot. I chose to update the bots user list.
126                                if(Command.IsLoggedIn(szUser))
127                                {
128                    std::string NewNick = sData.substr(sData.find(":", p2) + 1);
129                                        // On freenode I noticed the server sends an extra character
130                                        // at the end of the string, so we need to erase the last
131                                        // character of the string. if you have a problem with getting
132                                        // the last letter of your nick erased, then remove the - 1.
133                                        NewNick.erase(NewNick.length() - 1, 1);
134
135                                        for(std::list<_client*>::iterator i=Command._CLIENTS.begin(); i!=Command._CLIENTS.end();i++)
136                    {
137                        if((*i)->Name == szUser)
138                        {
139                                                (*i)->Name     = NewNick;
140                                                sIRC.Send_IRC_Channel(NewNick.c_str(), "I Noticed You Changed Your Nick, I Have Updated My Internal Database Accordingly.", true, "NOTICE");
141                                               
142                                                        // Figure why not output to the logfile, makes tracing problems easier.
143                                                        sIRC.iLog.WriteLog(" %s : %s Changed Nick To: %s", sIRC.iLog.GetLogDateTimeStr().c_str(), szUser.c_str(), NewNick.c_str());
144                        }
145                    }
146                                }
147
148            }
149            // someone was kicked from irc
150            if (CMD == "kick")
151            {
152                // extract the details
153                size_t p3 = sData.find(" ", p2 + 1);
154                size_t p4 = sData.find(" ", p3 + 1);
155                size_t p5 = sData.find(":", p4);
156                std::string CHAN = sData.substr(p2 + 1, p3 - p2 - 1);
157                std::string WHO = sData.substr(p3 + 1, p4 - p3 - 1);
158                std::string BY = sData.substr(p4 + 1, sData.size() - p4 - 1);
159                // if the one kicked was us
160                if(WHO == sIRC._Nick)
161                {
162                    // and autojoin is enabled
163                    // return to the channel
164                    if(sIRC._autojoinkick == 1)
165                    {
166                        SendIRC("JOIN " + CHAN);
167                        Send_IRC_Channel(CHAN, sIRC.kikmsg, true);
168                    }
169                }
170                else
171                {
172                    // if it is not us who was kicked we need to inform the clients someone
173                    // was removed from the channel
174                    // construct a message and send it to the players.
175                    Send_WoW_Channel(GetWoWChannel(CHAN).c_str(), "<IRC>[" + WHO + "]: Was Kicked From " + CHAN + " By: " + szUser);
176                }
177            }
178            // a private chat message was receieved.
179            if(CMD == "privmsg" || CMD == "notice")
180            {
181                // extract the values
182                size_t p = sData.find(" ", p2 + 1);
183                std::string FROM = sData.substr(p2 + 1, p - p2 - 1);
184                std::string CHAT = sData.substr(p + 2, sData.size() - p - 3);
185                // if this is our username it means we recieved a PM
186                if(FROM == sIRC._Nick)
187                {
188                    if(CHAT.find("\001VERSION\001") < CHAT.size())
189                    {
190                        Send_IRC_Channel(szUser, MakeMsg("\001VERSION MangChat %s ©2008 |Death|\001", "%s" , sIRC._Mver.c_str()), true, "PRIVMSG");
191                    }
192                    // a pm is required for certain commands
193                    // such as login. to validate the command
194                    // we send it to the command class wich handles
195                    // evrything else.
196                    Command.IsValid(szUser, FROM, CHAT, CMD);
197                }
198                else
199                {
200                    // if our name is not in it, it means we receieved chat on one of the channels
201                    // magchat is in. the first thing we do is check if it is a command or not
202                    if(!Command.IsValid(szUser, FROM, CHAT, CMD))
203                                        {
204                                                Send_WoW_Channel(GetWoWChannel(FROM).c_str(), IRCcol2WoW(MakeMsg(MakeMsg(GetChatLine(IRC_WOW), "$Name", szUser), "$Msg", CHAT)));
205                                        }
206                    // if we indeed receieved a command we do not want to display this to the players
207                    // so only incanse the isvalid command returns false it will be sent to all player.
208                    // the isvalid function will automaitcly process the command on true.
209                }
210            }
211            if(CMD == "mode")
212            {
213                // extract the mode details
214                size_t p3 = sData.find(" ", p2 + 1);
215                size_t p4 = sData.find(" ", p3 + 1);
216                size_t p5 = sData.find(" ", p4 + 1);
217                std::string CHAN = sData.substr(p2 + 1, p3 - p2 - 1);
218                std::string MODE = sData.substr(p3 + 1, p4 - p3 - 1);
219                std::string NICK = sData.substr(p4 + 1, p5 - p4 - 1);                       
220                                bool _AmiOp;
221                                _AmiOp = false;
222                                //A mode was changed on us
223                                if(NICK.c_str() == sIRC._Nick)
224                                        _AmiOp = true;
225
226                        }
227        }
228    }
229}
230
231// This function is called in Channel.h
232// based on nAction it will inform the people on
233// irc when someone leaves one of the game channels.
234// nAction is based on the struct CACTION
235void IRCClient::Handle_WoW_Channel(std::string Channel, Player *plr, int nAction)
236{
237    // make sure that we are connected
238    if(sIRC.Connected && (sIRC.BOTMASK & 1)!= 0)
239    {
240        if(Channel_Valid(Channel))
241        {
242            std::string GMRank = "";
243            std::string pname = plr->GetName();
244            bool DoGMAnnounce = false;
245            if (plr->GetSession()->GetSecurity() > 0 && (sIRC.BOTMASK & 8)!= 0)
246                DoGMAnnounce = true;
247            if (plr->isGameMaster() && (sIRC.BOTMASK & 16)!= 0)
248                DoGMAnnounce = true;
249            if(DoGMAnnounce)
250            {
251                switch(plr->GetSession()->GetSecurity())    //switch case to determine what rank the "gm" is
252                {
253                    case 0: GMRank = "";break;
254                    case 1: GMRank = "\0037"+sIRC.ojGM1;break;
255                    case 2: GMRank = "\0037"+sIRC.ojGM2;break;
256                    case 3: GMRank = "\0037"+sIRC.ojGM3;break;
257                    case 4: GMRank = "\0037"+sIRC.ojGM4;break;
258                    case 5: GMRank = "\0037"+sIRC.ojGM5;break;
259                }
260            }
261            std::string ChatTag = "";
262            switch (plr->GetTeam())
263            {
264                case 67:ChatTag.append("\0034");break;      //horde
265                case 469:ChatTag.append("\00312");break;    //alliance
266            }
267            std::string query = "INSERT INTO `IRC_Inchan` VALUES (%d,'"+pname+"','"+Channel+"')";
268            std::string lchan = "DELETE FROM `IRC_Inchan` WHERE `guid` = %d AND `channel` = '"+Channel+"'";
269            switch(nAction)
270            {
271                case CHANNEL_JOIN:
272                    Send_IRC_Channel(GetIRCChannel(Channel), MakeMsg(MakeMsg(MakeMsg(GetChatLine(JOIN_WOW), "$Name", ChatTag + plr->GetName()), "$Channel", Channel), "$GM", GMRank));
273                    WorldDatabase.PExecute(lchan.c_str(), plr->GetGUID());
274                    WorldDatabase.PExecute(query.c_str(), plr->GetGUID());
275                    break;
276                case CHANNEL_LEAVE:
277                    Send_IRC_Channel(GetIRCChannel(Channel), MakeMsg(MakeMsg(MakeMsg(GetChatLine(LEAVE_WOW), "$Name", ChatTag + plr->GetName()), "$Channel", Channel), "$GM", GMRank));
278                    WorldDatabase.PExecute(lchan.c_str(), plr->GetGUID());
279                    break;
280            }
281        }
282    }
283}
284
285// This function sends chat to a irc channel or user
286// to prevent the # beeing appended to send a msg to a user
287// set the NoPrefix to true
288void IRCClient::Send_IRC_Channel(std::string sChannel, std::string sMsg, bool NoPrefix, std::string nType)
289{
290    std::string mType = "PRIVMSG";     
291        if(Command.MakeUpper(nType.c_str()) == "NOTICE")
292                mType = "NOTICE";
293    if(Command.MakeUpper(nType.c_str()) == "ERROR" && (sIRC.BOTMASK & 32)!= 0)
294                mType = "NOTICE";
295    if(sIRC.Connected)
296    {
297        if(NoPrefix)
298            SendIRC(mType + " " + sChannel + " :" + sMsg);
299        else
300            SendIRC(mType + " #" + sChannel + " :" + sMsg);
301    }
302}
303
304// This function sends a message to all irc channels
305// that mangchat has in its configuration
306void IRCClient::Send_IRC_Channels(std::string sMsg)
307{
308    for(int i=1;i < sIRC._chan_count + 1;i++)
309        Send_IRC_Channel(sIRC._irc_chan[i], sMsg);
310}
311
312// This function is called in ChatHandler.cpp, any channel chat from wow will come
313// to this function, validates the channel and constructs a message that is send to IRC
314void IRCClient::Send_WoW_IRC(Player *plr, std::string Channel, std::string Msg)
315{
316    // Check if the channel exist in our configuration
317    if(Channel_Valid(Channel) && Msg.substr(0, 1) != ".")
318        Send_IRC_Channel(GetIRCChannel(Channel), MakeMsgP(WOW_IRC, Msg, plr));
319}
320
321void IRCClient::Send_WoW_Player(std::string sPlayer, std::string sMsg)
322{
323    normalizePlayerName(sPlayer);
324    if (Player* plr = ObjectAccessor::Instance().FindPlayerByName(sPlayer.c_str()))
325        Send_WoW_Player(plr, sMsg);
326}
327
328void IRCClient::Send_WoW_Player(Player *plr, string sMsg)
329{
330    WorldPacket data(SMSG_MESSAGECHAT, 200);
331    data << (uint8)CHAT_MSG_SYSTEM;
332    data << (uint32)LANG_UNIVERSAL;
333    data << (uint64)plr->GetGUID();
334    data << (uint32)0;
335    data << (uint64)plr->GetGUID();
336    data << (uint32)(sMsg.length()+1);
337    data << sMsg;
338    data << (uint8)0;
339    plr->GetSession()->SendPacket(&data);
340}
341
342// This function will construct and send a packet to all players
343// on the given channel ingame. (previuosly found in world.cpp)
344// it loops thru all sessions and checks if they are on the channel
345// if so construct a packet and send it.
346void IRCClient::Send_WoW_Channel(const char *channel, std::string chat)
347{
348    if(!(strlen(channel) > 0))
349        return;
350
351    #ifdef USE_UTF8
352        std::string chat2 = chat;
353        if(ConvertUTF8(chat2.c_str(), chat2))
354            chat = chat2;
355    #endif
356
357    HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
358    for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
359    {
360        if (itr->second && itr->second->GetSession()->GetPlayer() && itr->second->GetSession()->GetPlayer()->IsInWorld())
361        {
362            if(ChannelMgr* cMgr = channelMgr(itr->second->GetSession()->GetPlayer()->GetTeam()))
363            {
364                if(Channel *chn = cMgr->GetChannel(channel, itr->second->GetSession()->GetPlayer()))
365                {
366                    WorldPacket data;
367                    data.Initialize(SMSG_MESSAGECHAT);
368                    data << (uint8)CHAT_MSG_CHANNEL;
369                    data << (uint32)LANG_UNIVERSAL;
370                    data << (uint64)0;
371                    data << (uint32)0;
372                    data << channel;
373                    data << (uint64)0;
374                    data << (uint32) (strlen(chat.c_str()) + 1);
375                    data << IRCcol2WoW(chat.c_str());
376                    data << (uint8)0;
377                    itr->second->GetSession()->SendPacket(&data);
378                }
379            }
380        }
381    }
382}
383
384void IRCClient::Send_WoW_System(std::string Message)
385{
386    HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
387    for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
388    {
389        if (itr->second && itr->second->GetSession()->GetPlayer() && itr->second->GetSession()->GetPlayer()->IsInWorld())
390        {
391            WorldPacket data;
392            data.Initialize(CHAT_MSG_SYSTEM);
393            data << (uint8)CHAT_MSG_SYSTEM;
394            data << (uint32)LANG_UNIVERSAL;
395            data << (uint64)0;
396            data << (uint32)0;
397            data << (uint64)0;
398            data << (uint32) (strlen(Message.c_str()) + 1);
399            data << Message.c_str();
400            data << (uint8)0;
401            itr->second->GetSession()->SendPacket(&data);
402        }
403    }
404}
405void IRCClient::ResetIRC()
406{
407    SendData("QUIT");
408    Disconnect();
409}
410
411#define CHAT_INVITE_NOTICE 0x18
412
413// this function should be called on player login Player::AddToWorld
414void IRCClient::AutoJoinChannel(Player *plr)
415{
416    //this will work if at least 1 player is logged in regrdless if he is on the channel or not
417    // the first person that login empty server is the one with bad luck and wont be invited,
418    // if at least 1 player is online the player will be inited to the chanel
419
420    std::string m_name = sIRC.ajchan;
421    WorldPacket data;
422    data.Initialize(SMSG_CHANNEL_NOTIFY, 1+m_name.size()+1);
423    data << uint8(CHAT_INVITE_NOTICE);
424    data << m_name.c_str();
425
426    HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
427    for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
428    {
429        if (itr->second && itr->second->GetSession()->GetPlayer() && itr->second->GetSession()->GetPlayer()->IsInWorld())
430        {
431            data << uint64(itr->second->GetGUID());
432            break;
433        }
434    }
435    plr->GetSession()->SendPacket(&data);
436}
Note: See TracBrowser for help on using the browser.