Thread: RicBot

  1. #1
    Registered User
    Join Date
    Jan 2006
    Posts
    63

    RicBot

    Hello everybody

    A long time ago I started a project called RicBot http://cboard.cprogramming.com/showthread.php?t=75223

    Which I just left and didn't bother with anymore, I decided I would go back to it again though. So today I added a little piece of code which will know it has timedout if it didn't send or receive any data within 150 seconds.

    There is something I want to do to it, but I'm not sure how, so I'm looking for suggestions. As it is, it receives the data and then loops through the main body of code. This is not a very good method. The data should be parsed into strings. So that each string is a single line of data. Like a little parsing function which seperates strings if they have '\r', '\n' or a combination of both.

    So that once it has parsed the string, into several strings it loops through the main body, does what it needs with the string, then loops again with the next string. Also if the data doesn't end with a '\r', '\n' or combination of both, it will save that piece of data until the next time it receives data through the socket, and will add the new data received from the socket to the end of the current string. Then parse that string.

    It doesn't have to split it into seperate strings I guess, there is no point in having an array of them I guess. Probably just the main string which receives the data, then that goes into another string to store it. It parses it for lines, puts the first line into the main string which will go through the code, and then stores the rest until it comes back again, then it puts the next string into the main string and loops through the code etc.


    I am not sure how to do this, here is the code I got so far.

    Code:
    /* This is RicBot, written by John */
    
    #pragma warning(disable: 4786)
    #include <winsock2.h>
    #pragma comment(lib,"WS2_32.lib")
    #include <vector>
    #include <string>
    #include <sstream>
    #include <iostream>
    /////////////////////////////
    //This isn't multithreaded yet, so the code below isn't
    //needed but I have added it from an older program I made 
    //to save me time ;)
    //#include <process.h>
    //Using MSVC++
    //For Win32 Release
    //Project -> Settings -> C/C++ -> Code Generation
    //Use-Runtime Library -> Multithreaded
    //#pragma comment(lib, "libcmt.lib")
    //For Win32 Debug
    //Project -> Settings -> C/C++ -> Code Generation
    //Use-Runtime Library -> Debug Multithreaded
    //#pragma comment(lib, "libcmtd.lib")
    ///////////////////////////////
    SOCKET IRC_socket; //Global
    
    using namespace std;
    
    //#define RicBot
    #define AUTHENTICATION
    
    //Function Prototypes
    int CreateSock();
    int ConnectIRCSock(int Port, string IP);
    int SendIRC_func(string SendData_string);
    int RecvIRC_func(string &RecvData_string);
    int OnConnect(string BotNick, string IP);
    int ReConnect(int Port, string IP, string BotNick);
    int CheckForTimeout(int Port, string IP, string BotNick);
    int CheckForPing(string RecvData_string);
    int Get_Nick_Ident_Host(string RecvData_string);
    int JoinChannel(string Channel);
    int MessageChannel(string Message, string Channel);
    string RandomNick(string BotNick);
    
    struct IdentNickHostInfo {
    	string Nick;
    	string Ident;
    	string Host;
    } AuthInfo;
    
    int PingTime = 0, TimeElapsed = 0;
    
    #ifdef RicBot
    #pragma comment(linker,"/subsystem:windows")
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    				   LPSTR lpCmdLine, int nCmdShow){
    #endif
    #ifndef RicBot
    #pragma comment(linker,"/subsystem:console")
    int main(int argc, char* argv[]){
    #endif
    
    	string SendData_string, RecvData_string;
    	int Port = 6667;
    	string IP  = "";
    	string BotNick = "";
    	string Channel = "";
    	//Nick/Ident/Host for Auth
    	string AuthNick = "";
    	string AuthIdent = "";
    	string AuthHost = "";
    	string CMDprefix = "!";
    	//our commands
    	string VersionCMD = "version";
    	string Version = "RicBot";
    	string ReconnectCMD = "reconnect";
    	string KillCMD = "stop";
    	string RawCMD = "raw";
    
    
    
    	CreateSock();
    	ConnectIRCSock(Port, IP);
    	OnConnect(BotNick, IP);	
    	PingTime = GetTickCount()/1000;
    
    	while(1){
    			
    		CheckForTimeout(Port, IP, BotNick);
    
    		if(RecvIRC_func(RecvData_string) == -1){
    			Sleep(10000);
    			ReConnect(Port, IP, BotNick);
    		}
    
    #ifndef RicBot
    		cout<<RecvData_string;
    #endif
    		CheckForPing(RecvData_string);
    		
    
    		if(RecvData_string[0] == ':'){
    			
    			//--------------------------
    			string strData = "";
    			vector<string> SplitData;
    			
    			int i=0;
    			while(RecvData_string[i] != '\r'){
    				if(RecvData_string[i] == ' '){
    					if(RecvData_string[i+1] != '\r'){
    						SplitData.push_back(strData);
    						strData = "";
    						i++;
    					}
    				}
    				strData = strData + RecvData_string[i];
    				i++;
    			}
    			SplitData.push_back(strData);
    			//--------------------------
    			
    			//here our server commands go.
    			if(SplitData.size() >= 2){
    				//We can join a channel now.
    				if(SplitData[1] == "001"){
    					JoinChannel(Channel);
    				}
    				//nick is in use, so change it.
    				if(SplitData[1] == "433"){
    					//(BotNick=RandomNick(BotNick));
    					OnConnect(BotNick=RandomNick(BotNick), IP);
    				}
    
    			}
    			if(SplitData.size() >= 4){
    				if(SplitData[1] == "KICK" && SplitData[3] == BotNick){
    					Sleep(3000);
    					JoinChannel(Channel);
    				}
    			}
    
    			//gets the nick/ident/host of user sending message. 
    			if(Get_Nick_Ident_Host(RecvData_string) == 0){
    				
    				//here are our user commands
    				if(SplitData.size() >= 4){
    					
    					//this line will remove the :
    					SplitData[3] = SplitData[3].substr(1);
    					//will check if our first character is our prefix
    					if(SplitData[3][0] == CMDprefix[0]){
    						
    						//this will remove our prefix.
    						SplitData[3] = SplitData[3].substr(1);
    						
    						//--------------------------
    						//commands without Auth go here
    						// if they have no arguments
    						if(SplitData.size() >= 4){
    							
    							if(SplitData[3] == VersionCMD){
    								MessageChannel(Version, Channel);
    							}
    						}
    						
    						//Here is our authentication check.
    #ifdef AUTHENTICATION
    						if(AuthInfo.Nick == AuthNick){
    							if(AuthInfo.Ident == AuthIdent){
    								if(AuthInfo.Host == AuthHost){
    #endif
    									//commands with Auth go here
    									
    									//-----------------------
    									//if command has 0 arguments
    									if(SplitData.size() == 4){
    
    										if(SplitData[3] == ReconnectCMD){
    											string QUIT = "QUIT Reconnecting...\r";
    											SendIRC_func(QUIT);
    											ReConnect(Port, IP, BotNick);
    										}
    										if(SplitData[3] == KillCMD){
    											exit(0);
    										}
    									}
    									
    									//--------------------------
    									//if command has 1 argument or more
    									if(SplitData.size() >= 5){
    										
    										if(SplitData[3] == RawCMD){
    											int sizerawmessage = (int)SplitData.size() - 4;
    											ostringstream rawmessage;
    											for(int i = 0; i < sizerawmessage; i++){
    												rawmessage<<SplitData[i+4]<<" ";
    											}
    											rawmessage<<"\r";
    											SendIRC_func(rawmessage.str());
    										}
    									}
    								}
    							}
    						}
    					}
    #ifdef AUTHENTICATION	
    				}
    			}
    		}
    #endif
    
    		RecvData_string = '\0';
    	}
    
    	
    	return 0;
    }
    
    ////////////////////////////////////////
    int CreateSock(){
    
    	WSADATA wsaData;
    	
    	int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
    	if ( iResult != NO_ERROR )
    		return -1;
    	
    	return 0;
    }
    
    ////////////////////////////////////////
    int ConnectIRCSock(int Port, string host){
    
    	IRC_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    	
    	if (IRC_socket == INVALID_SOCKET){
    		WSACleanup();
    		return -2;
    	}
    
    	sockaddr_in remotehost;
    
    	remotehost.sin_family = AF_INET;
    	remotehost.sin_addr.s_addr = inet_addr(host.c_str());
    	remotehost.sin_port = htons(Port);
    
    	if (connect(IRC_socket, (SOCKADDR*) &remotehost, sizeof(remotehost) ) == SOCKET_ERROR){
    		closesocket(IRC_socket);
    		WSACleanup();
    		return -1;
    	}
    
    	return 0;
    }
    
    ////////////////////////////////////////
    int SendIRC_func(string SendData_string){
    	
    	if (send(IRC_socket, SendData_string.c_str(), (int)SendData_string.size(), 0 ) <= 0){
    		WSACleanup();
    		return -1;
    	}
    
    	PingTime = GetTickCount()/1000;
    	return 0;
    
    }
    
    ////////////////////////////////////////
    int RecvIRC_func(string &RecvData_string){
    	
    	char TempBuffer[1025]; //extra char for null terminating character
    	int BytesRecv;
    	if ((BytesRecv = recv(IRC_socket, TempBuffer, 1024, 0 )) <= 0){
    		WSACleanup();
    		return -1;
    	}
    
    	TempBuffer[BytesRecv] = '\0';
    	RecvData_string = TempBuffer;
    	PingTime = GetTickCount()/1000;
    
    	return 0;
    
    }
    
    ////////////////////////////////////////
    int OnConnect(string BotNick, string IP){
    
    	ostringstream tempstr;
    	tempstr <<"Nick "<<BotNick<<'\n';
    	SendIRC_func(tempstr.str());
    	tempstr.str("");
    	tempstr<<"USER "<<BotNick<<" \"\" \""<< IP <<
    		"\" :"<<BotNick<<'\n';
    	SendIRC_func(tempstr.str());
    	
    	return 0;
    }
    
    ////////////////////////////////////////
    int ReConnect(int Port, string IP, string BotNick){
    		
    	closesocket(IRC_socket);
    
    	CreateSock();
    	ConnectIRCSock(Port, IP);
    	OnConnect(BotNick, IP);
    	
    	return 0;
    }
    
    ////////////////////////////////////////
    int CheckForTimeout(int Port, string IP, string BotNick){
    
    	if(TimeElapsed < PingTime+150){
    		TimeElapsed = GetTickCount()/1000;
    		return -1;
    	}
    	
    	Sleep(10000);
    	ReConnect(Port, IP, BotNick);
    	PingTime = GetTickCount()/1000;
    	return 0;
    }
    
    ////////////////////////////////////////
    int CheckForPing(string RecvData_string){
    	
    	string ServerPing;
    	ostringstream SendPong;
    	int i = 0;
    	
    	//////////////////////////////////////////////////////
    	/*if(RecvData_string[i] == 'P' && RecvData_string[i+1] == 'I' &&
    		RecvData_string[i+2] == 'N' && RecvData_string[i+3] == 'G' ){
    		i += 5;
    		while(RecvData_string[i] != '\r'){
    			ServerPing = ServerPing + RecvData_string[i];
    			i++;
    		}
    		ServerPing = ServerPing + '\0';
    		SendPong << "PONG " << ServerPing << '\n';
    		SendIRC_func(SendPong.str());
    		return -1;
    	}*/
    	if(RecvData_string[i] == 'P' && RecvData_string[i+1] == 'I' &&
    		RecvData_string[i+2] == 'N' && RecvData_string[i+3] == 'G' ){
    
    		RecvData_string[1] = 'O';
    		SendIRC_func(RecvData_string);
    	}
    
    
    	//////////////////////////////////////////////////////
    	while(i < (int)RecvData_string.size()){
    		
    		if(RecvData_string[i] == '\r' && RecvData_string[i+1] == '\n' &&
    			RecvData_string[i+2] == 'P' && RecvData_string[i+3] == 'I' &&
    			RecvData_string[i+4] == 'N' && RecvData_string[i+5] == 'G'){
    
    			i += 7;
    			while(RecvData_string[i] != '\r'){
    				ServerPing = ServerPing + RecvData_string[i];
    				i++;
    			}
    			ServerPing = ServerPing + '\0';
    			SendPong << "PONG " << ServerPing << '\n';
    			SendIRC_func(SendPong.str());
    			return -2;
    		}
    		i++;
    	}
    	//////////////////////////////////////////////////////
    
    	return 0;
    }
    
    ////////////////////////////////////////
    int Get_Nick_Ident_Host(string RecvData_string){
    	
    	int i = 1;
    	AuthInfo.Nick = "";
    	AuthInfo.Ident = "";
    	AuthInfo.Host = "";
    	////////////////////////////////////////////
    	while(RecvData_string[i] != '!'){
    		
    		AuthInfo.Nick = AuthInfo.Nick + RecvData_string[i];
    		i++;
    		if(i == RecvData_string.size()){
    			return -1;
    		}
    	}
    	////////////////////////////////////////////
    	i++;
    	while(RecvData_string[i] != '@'){
    		
    		AuthInfo.Ident = AuthInfo.Ident + RecvData_string[i];
    		i++;
    		if(i == RecvData_string.size()){
    			return -2;
    		}
    	}
    	////////////////////////////////////////////
    	i++;
    	while(RecvData_string[i] != ' '){
    		
    		AuthInfo.Host = AuthInfo.Host + RecvData_string[i];
    		i++;
    		if(i == RecvData_string.size()){
    			return -3;
    		}
    	}
    
    	//cout<<"Nick is "<<AuthInfo.Nick<<endl;
    	//cout<<"Ident is "<<AuthInfo.Ident<<endl;
    	//cout<<"Host is "<<AuthInfo.Host<<endl;
    	return 0;
    }
    
    ////////////////////////////////////////
    int JoinChannel(string Channel){
    
    	ostringstream joinchan;
    	joinchan << "JOIN " << Channel << "\r";
    	SendIRC_func(joinchan.str());
    
    	return 0;
    }
    
    ////////////////////////////////////////
    int MessageChannel(string Message, string Channel){
    
    	ostringstream ChannelMessage;
    	ChannelMessage << "PRIVMSG "<<Channel<< " :" <<Message<<"\r";
    	SendIRC_func(ChannelMessage.str());
    	return 0;
    }
    
    ////////////////////////////////////////
    string RandomNick(string BotNick){
    	
    	srand((unsigned)GetTickCount());
    	int randomNum = rand()%10000;
    	ostringstream Nick;
    	Nick << BotNick << randomNum;
    
    	return Nick.str();
    }
    (I didn't upload the code in a .cpp file because it doesn't keep the line breaks the way they are.)

  2. #2
    Registered User
    Join Date
    Jan 2006
    Posts
    63
    I just realised that ping timeout checking code wont work. Because it will just keep waiting for data at recv(), so it would need an extra thread I guess which checks the time and uses a global variable for the time that has gone past. So that when SendIRC_func() RecvIRC_func() complete successfully they reset the time in the global variable and it starts again, I think that would work alright. Not very difficult, but the more important thing is the parselines functions

  3. #3
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    Heres how I laid things out for my bot. This just handles "\r\n" commands.
    Code:
    void IrcBot::Process()
    {
        std::string easyHandle;
        int recvAmt;
        char recvData[1024] = { '\0' };    
        
        while(1)
        {                   
            recvAmt = recv(serverSocket, recvData, 1023, 0);
            
            if(recvAmt > 0 && recvAmt < 1023)
            {
                recvData[recvAmt] = '\0';
            }    
            
            easyHandle = recvData;
            
            HandleRecv(easyHandle);
            
            //Clear out our stuff.
            std::memset(recvData,'\0',1024);
            
            //Play nice with cpu
            Sleep(1);
        }    
    }
    
    
    void IrcBot::HandleRecv(const std::string &toRecv)
    {
        size_t len = toRecv.length();
        size_t index = 0;
        for(size_t i = 0; index < len && i != std::string::npos; index = i + 2)
        {
            i = toRecv.find("\r\n", index);
            //Make sure we are all good
    	if(i != std::string::npos)
            {
    	    //Send it to get assigned and be parsed
    	    Send_Cmd(toRecv, index, i - index);
    	}
        }
    }
    
    void IrcBot::Send_Cmd(const std::string &cmd, size_t index, size_t len)                   
    {
        std::string temp;
        temp.assign(cmd, index, len);
        //Parse that sucka
        toLower(temp);
        Parse_Cmd(temp);
    }
    Last edited by prog-bman; 06-13-2006 at 01:34 AM.
    Woop?

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    I just saw 10 closing braces in a row and gave up bothering to read any more.

    Step 1 - learn how to structure code.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Jan 2006
    Posts
    63
    Thank you for your code example.

    I am curious, I don't see where you save any data which does not end with \r\n and append the new data you receive onto the end of it. How come you didn't choose to do this in your code?

  6. #6
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    Well as far as I know IRC commands are all ended with "\r\n" and this is an IRC bot so I basically chop up all the commands and then send them to my Parse_Cmd which does it's thing with the IRC message. Basically grabs the nick, channel, IRC Command(PING, NICK, etc), and the msg sent aka Text user typed.
    Woop?

  7. #7
    Registered User
    Join Date
    Jan 2006
    Posts
    63
    But the server can send data in chunks, for example messages from several users at a time may be sent from the server in one go. You will receive these messages all together as you know which is why they need parsing. But there may be too much data for it to send in one go, so it might send most of it in one go, and there may be a little bit left which it needs to send next time.

    Which means you might get 2 full messages and the beginning of the 3rd message but no end of it. The end of the message which has the \r\n will come next time. So after the bot has parsed the first 2 lines, the bot should check to see if the 3rd line ends with \r\n and if it doesn't, assume it is a partial string. Save the data from the partial string and when the data from the next recv() comes, append it to the end and start parsing it again.

    You may only miss the occasional message or command, but there will be times (regardless of how unoften) you may miss a full PING line, and then the bot will time out.

  8. #8
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    I see what you are saying. Fairly easy to implement I just honestly never thought about that before.
    Woop?

  9. #9
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    Have you looked into select() ? select() basically takes a list of sockets (here your list would only have 1 socket in it) and waits until they are either ready for reading, writing, or have an error. It can return immediately or sleep until something happens.

    And instead of character1 == 'P' && character2 == 'I' && 'N', etc etc. why not use the == operator for strings? Take a substring to get IRC the command. Then:
    Code:
    if(command == "PING") { blah }
    (Really... CBoard forces me to put the above in [code] tags?)
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. RicBot Base and RicBot Google.
    By John_ in forum C++ Programming
    Replies: 3
    Last Post: 02-07-2006, 01:16 PM