PDA

View Full Version : IRC Bot [c99] cross platfrom[unix][windows]



Yakman
05-02-2008, 03:43 PM
IRC Bot by yakman

Joins one server and one channel
has commands which start with a COMMAND_PREFIX, default is '!'

say [message] - repeats [message] back to the channel
fact - parses a website and says a fact
slogan - parses a website and says an advertising slogan
quote - parses a website and says a quote
insult [name] - parses a website and insults [name]

Compiles on unix and windows,
tested with GCC on unix and MinGW on windows



<yakman> !fact
<slurry_> Nurses and doctors wash their hands between patients less than one-third of the time.
<yakman> !quote
<slurry_> "People demand freedom of speech to make up for the freedom of thought which they avoid." - Soren Aabye Kierkegaard (1813-1855)
<yakman> !say hello world
<slurry_> hello world
<yakman> !slogan
<slurry_> Refreshes the Anxiety Other Beers Cannot Reach
<yakman> !insult Cowie`
<slurry_> Cowie`: I'd like to see things from your point of view but I can't seem to get my head that far up my ass.



i was wondering, is there a way you can list which libraries on windows to link, so you can write


gcc ircbot.c


instead of



gcc -L"lib\libws2_32.a" ircbot.c



heres the bot


/*
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 3 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, see <http://www.gnu.org/licenses/>.
*/

/*
IRC Bot by yakman

Joins one server and one channel
has commands which start with a COMMAND_PREFIX, default is '!'

say [message] - repeats [message] back to the channel
fact - parses a website and says a fact
slogan - parses a website and says an advertising slogan
quote - parses a website and says a quote
insult [name] - parses a website and insults [name]

Compiles on unix and windows,
tested with GCC on unix and MinGW on windows

To compile on unix
gcc -std=c99 -o ircbot ircbot.c

Compiling on windows varies, one thing you have to do is to link the the ws2_32 library
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//unix specific stuff
#ifdef __unix__

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>

#else

//windows specific stuff
#ifdef __WIN32__

#include <winsock2.h>

#else
#error "Only compiles on unix and windows"
#endif
#endif


//stuff to change
#define IRC_SERVER "irc.dairc.net"
#define IRC_PORT 6667

#define NICK "slurry_"
#define CHANNEL "#bots"

//which hostname can !quit the bot
#define MASTER_HOST "commandante.che.guevara"

//need the master_host for all commands?
//#define NEED_MASTER 1

#define COMMAND_PREFIX '!'

#define BUFFER_SIZE 64


//thesurrealist.co.uk/slogan.cgi
//www.randomfunfacts.com
//www.quotability.com
//www.randominsults.net

//no IRC stuff below yet, only the website parsing
char* downloadHTTP(char* hostName, char* page) {

struct hostent* host = gethostbyname(hostName);
if(host == NULL) {
#ifdef __unix__
herror("Error resolving host name");
#else
printf("Error resolving host name. errorcode: %i\n", WSAGetLastError());
#endif
return NULL;
}

struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = ((struct in_addr*)host->h_addr)->s_addr;

int sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < 0) {
perror("failed to open socket");
return NULL;
}

if(connect(sock_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("failed to connect");
return NULL;
}

char* one = "GET /";
char* three = " HTTP/1.1\r\nHost: ";
char* five = "\r\nUser-Agent: Mozilla/5.0 (Linux; X11; UTF-8)\r\nConnection: Close\r\n\r\n";
int httplen = strlen(one) + strlen(page) + strlen(three) + strlen(hostName) + strlen(five);
char* http = (char*)malloc(httplen + 1);
*http = '\0';
strcat(http, one);
strcat(http, page);
strcat(http, three);
strcat(http, hostName);
strcat(http, five);
send(sock_fd, http, httplen, 0);
free(http);

int pos = 0;
int len = BUFFER_SIZE;
char* buffer = (char*)malloc(len);
while(1) {
char buf[BUFFER_SIZE];
int reclen = recv(sock_fd, buf, BUFFER_SIZE, 0);
if(reclen <= 0)
break;
if(reclen + pos > len-1) {
len = (len * 3 / 2) + 1;
buffer = (char*)realloc(buffer, len);
}
memcpy(buffer + pos, buf, reclen);
pos += reclen;
}
buffer[pos] = '\0';

#ifdef __unix__
close(sock_fd);
#else
closesocket(sock_fd);
#endif
char* headerEnd = strstr(buffer, "\r\n\r\n");
char* payloadEnd = headerEnd + strlen(headerEnd);
char* result = (char*)malloc(payloadEnd - headerEnd + 1);
memcpy(result, headerEnd, payloadEnd - headerEnd);
result[payloadEnd - headerEnd] = '\0';
free(buffer);
buffer = NULL;
return result;
}

char* getFact(void) {
char* page = downloadHTTP("www.randomfunfacts.com", "");
if(page == NULL)
return NULL;

char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}

char* getSlogan(void) {
char* page = downloadHTTP("thesurrealist.co.uk", "slogan.cgi");
if(page == NULL)
return NULL;
char* first = strstr(page, "class=\"shirtaw\">");
if(first == NULL)
return NULL;
first += 16;
char* last = strstr(first, "</a>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}

char* getInsult(void) {
char* page = downloadHTTP("www.randominsults.net", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}

char* getQuote(void) {
char* page = downloadHTTP("www.quotability.com", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}

//IRC processing happens below

int connectToIRC(char* server, int port) {

printf("Connecting to %s:%i\n", server, port);
struct hostent* host = gethostbyname(server);
if(host == NULL) {
#ifdef __unix__
herror("Error resolving host name");
#else
printf("Error resolving host name. errorcode: %i\n", WSAGetLastError());
#endif
return -1;
}

struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = ((struct in_addr*)host->h_addr)->s_addr;

int sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < 0) {
perror("failed to open socket");
return -1;
}

if(connect(sock_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("failed to connect");
return -1;
}
return sock_fd;
}

struct BufferedSocketReader {
int sock_fd;
int closed; //is set if the file is closed AND the buffer is empty
char* buffer; //note buffer isnt null-terminated
int pos;
int len;
};

void sendData(struct BufferedSocketReader* reader, char* line) {
//makes a copy of the line with a linebreak added
int lbLen = strlen(line) + 2;
char* lineLB = (char*)malloc(lbLen);
memcpy(lineLB, line, lbLen - 1);
lineLB[lbLen - 2] = '\n';
lineLB[lbLen - 1] = '\0';

printf("=> %s", lineLB);
send(reader->sock_fd, lineLB, strlen(lineLB), 0); //TODO check return value and resend if not everything is sent
free(lineLB);
}

void closeSocketReader(struct BufferedSocketReader* reader) {
#ifdef __unix__
close(reader->sock_fd);
#else
closesocket(reader->sock_fd);
#endif
reader->sock_fd = -1;
}

void fillSocketBuffer(struct BufferedSocketReader* reader) {
if(reader->sock_fd == -1) {
reader->closed = 1;
return;
}

char buf[BUFFER_SIZE];
int recLen = recv(reader->sock_fd, buf, BUFFER_SIZE, 0);
if(recLen < 0) {
closeSocketReader(reader);
}
int newLen = (reader->len - reader->pos) + recLen; //new length is the length already in the buffer, and length of just recved
char* newBuf = (char*)malloc(newLen);
memset(newBuf, '\0', newLen);
memcpy(newBuf, reader->buffer + reader->pos, reader->len - reader->pos); //copy the old buffer into new
memcpy(newBuf + reader->len - reader->pos, buf, recLen); //copy receved buffer into new buffer
free(reader->buffer);
reader->buffer = newBuf;
reader->len = newLen;
reader->pos = 0;
}

char* readNextLine(struct BufferedSocketReader* reader) {
//bug, if we reach the EOF and there is no line break, the data in the buffer will be discarded
//not likely to happen on irc protocol though
while(!reader->closed) {
for(int i = reader->pos; i < reader->len-1; i++) { //-1 to allow for the check for double linebreak
if(reader->buffer[i] == '\r' || reader->buffer[i] == '\n') {
int len = i - reader->pos;
char* result = (char*)malloc(len + 1);
memcpy(result, reader->buffer + reader->pos, len);
result[len] = '\0';
reader->pos = i + 1; //skip the char you just read
//sometimes line breaks come in twos
if(reader->buffer[reader->pos] == '\r' || reader->buffer[reader->pos] == '\n')
reader->pos++;
return result;
}
}
//didnt find \r\n, need to read more data into the buffer
fillSocketBuffer(reader);
}
//socket is closed and buffer is empty
return NULL;
}

void handleCommand(struct BufferedSocketReader* reader, char* nick, char* user, char* host, char* cmd, char* chan, char* text) {

#ifdef NEED_MASTER
if(strstr(host, MASTER_HOST) == NULL)
return;
#endif

if(strstr(chan, CHANNEL) == NULL) //someone sent private message, ie. not from channel
return;

if(strstr(text, "quit") == text && strstr(host, MASTER_HOST) != NULL) {
sendData(reader, "QUIT :leaving");
printf("Quitting\n");
closeSocketReader(reader);
}else
if(strstr(text, "say") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(text + 4) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, text + 4);
sendData(reader, sayline);
free(sayline);
}else
if(strstr(text, "fact") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* fact = getFact();
if(fact == NULL)
fact = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(fact) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, fact);
sendData(reader, sayline);
free(fact);
free(sayline);
}else
if(strstr(text, "slogan") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* slogan = getSlogan();
if(slogan == NULL)
slogan = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(slogan) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, slogan);
sendData(reader, sayline);
free(slogan);
free(sayline);
}else
if(strstr(text, "insult") == text) {

char* target = NULL;
if(strchr(text, ' ') != NULL) {
char* t = strchr(text, ' ');
*t = '\0';
target = t + 1;

//if there is a trailing ' ' we have to remove
if(strchr(target, ' ') != NULL) {
char* t = strchr(target, ' ');
*t = '\0';
}
}

char* cmd = "PRIVMSG ";
char* colon = " :";
char* insult = getInsult();
if(insult == NULL)
insult = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) +
(target == NULL ? 0 : strlen(target) + 2) + strlen(insult) + 1;
//add 2 if there is a target, for the colon and space

char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
if(target != NULL) {
strcat(sayline, target);
strcat(sayline, ": ");
}
strcat(sayline, insult);

sendData(reader, sayline);
free(insult);
free(sayline);
}else
if(strstr(text, "quote") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* quote = getQuote();
if(quote == NULL)
quote = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(quote) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, quote);
sendData(reader, sayline);
free(quote);
free(sayline);
}
}

void handleLine(struct BufferedSocketReader* reader, char* line) {
printf("<= %s\n", line);

char* lineTok = (char*)malloc(strlen(line) + 1);
strcpy(lineTok, line);

if(strstr(line, "PING") == line) {
line[1] = 'O';
sendData(reader, line);
printf("Replied to ping\n");
free(lineTok);
return;
}

//has the ":nick!username@host" format
//also works for other stuff
if(*lineTok != ':') {
free(lineTok);
return;
}

char* nickuserhost = strtok(lineTok + 1, " ");
char* cmd = strtok(NULL, " ");
char* chan = strtok(NULL, " ");
char* text = strtok(NULL, "\0");

char* nick = NULL;
char* user = NULL;
char* host = NULL;
//split nick, user and host
if(nickuserhost != NULL && strchr(nickuserhost, '!') != NULL && strchr(nickuserhost, '@') != NULL) {
nick = strtok(nickuserhost, "!");
user = strtok(NULL, "@");
host = strtok(NULL, "\0");
}
if(text != NULL && *text == ':') //remove the first ':' on chat
text++;

//below is the using of the strings
if(strcmp(cmd, "PRIVMSG") == 0 && *text == COMMAND_PREFIX) { //someone has spoken
handleCommand(reader, nick, user, host, cmd, chan, text + 1);
}

if(strcmp(cmd, "KICK") == 0 && strstr(text, NICK) == text) {
char* header = "JOIN "; //send "JOIN #channel"
char* chanLine = (char*)malloc(strlen(header) + strlen(CHANNEL) + 1);
*chanLine = '\0';
strcat(chanLine, header);
strcat(chanLine, CHANNEL);
sendData(reader, chanLine);
free(chanLine);
}

free(lineTok);
}

int main(int argc, char** argv) {

#ifdef __WIN32__
WSADATA wsaData;
if(WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
printf("Error starting winsock\n");
return EXIT_FAILURE;
}
#endif

struct BufferedSocketReader reader;
reader.sock_fd = connectToIRC(IRC_SERVER, IRC_PORT);
reader.closed = 0;
reader.pos = 0;

sendData(&reader, "USER user 8 * :real");

char* header = "NICK "; //send "NICK mynick"
char* nickLine = (char*)malloc(strlen(header) + strlen(NICK) + 1);
*nickLine = '\0';
strcat(nickLine, header);
strcat(nickLine, NICK);
sendData(&reader, nickLine);
free(nickLine);

header = "JOIN "; //send "JOIN #channel"
char* chanLine = (char*)malloc(strlen(header) + strlen(CHANNEL) + 1);
*chanLine = '\0';
strcat(chanLine, header);
strcat(chanLine, CHANNEL);
sendData(&reader, chanLine);
free(chanLine);

char buf[BUFFER_SIZE];
reader.len = recv(reader.sock_fd, buf, BUFFER_SIZE, 0);
reader.buffer = (char*)malloc(reader.len); //initalise buffer
memcpy(reader.buffer, buf, reader.len);

while(!reader.closed) {
char* line = readNextLine(&reader);
if(line == NULL)
continue;
handleLine(&reader, line);
free(line);
}

printf("Socket closed or buffer empty\nExiting...\n");
#ifdef __WIN32__
WSACleanup();
system("PAUSE");
#endif
return EXIT_SUCCESS;
}

EvilChicken!
05-02-2008, 03:48 PM
mixster showed me a SCAR bot which he claimed you made the core for..?
Why not SCAR? His bot was really fun to use..

Hugolord
05-02-2008, 04:18 PM
C++ = faster/ more efficient?

Yakman
05-02-2008, 04:47 PM
EvilChicken!, scar is easier, therefore i learn less,
also who says a c bot isnt fun?
i did make the core for his bot, it wasn't very difficult because scar handles overflows and stuff like that by crashing, so they are much much easier to find

Hugolord, this isnt c++, its c, (alright they are very similar), also i wasn't looking for speed or efficiency

Hugolord
05-05-2008, 08:44 PM
EvilChicken!, scar is easier, therefore i learn less,
also who says a c bot isnt fun?
i did make the core for his bot, it wasn't very difficult because scar handles

Hugolord, this isnt c++, its c, (alright they are very similar), also i wasn't looking for speed or efficiency

ugh I can never tell the difference, well if you weren't looking for speed/efficiency why C?

Bobarkinator
05-06-2008, 12:36 AM
ugh I can never tell the difference, well if you weren't looking for speed/efficiency why C?



Perhaps for the same reason that Ben made SMART? To lean the language better. Ben didn't know C++ at all, except the syntax before he made SMART(albeit that most of it is made in Java)

boberman
05-06-2008, 04:27 PM
ugh I can never tell the difference, well if you weren't looking for speed/efficiency why C?

The difference is that C doesn't use objects (classes) while C++ does. Actuallly quite a huge difference.

All C should compile with a C++ compiler, but it isn't true visa-versa

To the original question, that is what MakeFiles are all about They allow you to compile a whole bunch of stuff and will determine which files need to be linked based on configuration. Go look up autoconf and autoMake. I believe they have windows derivatives.