C Board  

Go Back   C Board > General Programming Boards > C++ Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 06-11-2006, 07:17 AM   #1
Registered User
 
Join Date: Jan 2006
Posts: 63
RicBot

Hello everybody

A long time ago I started a project called RicBot RicBot Base and RicBot Google.

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.)
John_ is offline   Reply With Quote
Old 06-13-2006, 01:24 AM   #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
John_ is offline   Reply With Quote
Old 06-13-2006, 01:32 AM   #3
Sweet
 
Join Date: Aug 2002
Location: Tucson, Arizona
Posts: 1,696
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);
}
__________________
Woop?

Last edited by prog-bman; 06-13-2006 at 01:34 AM.
prog-bman is offline   Reply With Quote
Old 06-13-2006, 01:37 AM   #4
and the hat of Jobseeking
 
Salem's Avatar
 
Join Date: Aug 2001
Location: The edge of the known universe
Posts: 21,648
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.

Salem is offline   Reply With Quote
Old 06-13-2006, 01:58 AM   #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?
John_ is offline   Reply With Quote
Old 06-13-2006, 02:19 AM   #6
Sweet
 
Join Date: Aug 2002
Location: Tucson, Arizona
Posts: 1,696
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?
prog-bman is offline   Reply With Quote
Old 06-13-2006, 03:26 AM   #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.
John_ is offline   Reply With Quote
Old 06-13-2006, 03:34 AM   #8
Sweet
 
Join Date: Aug 2002
Location: Tucson, Arizona
Posts: 1,696
I see what you are saying. Fairly easy to implement I just honestly never thought about that before.
__________________
Woop?
prog-bman is offline   Reply With Quote
Old 06-13-2006, 06:52 PM   #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)
Cactus_Hugger is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
RicBot Base and RicBot Google. John_ C++ Programming 3 02-07-2006 01:16 PM


All times are GMT -6. The time now is 09:14 AM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22