Simple C++ IRC Bot for Linux

This uses a tiny socket class I made that can connect to a host through a hostname and port. The In and Out functions send and receive data line by line (terminated by CRLF). There’s a special output stream operator for sending data. With this operator the data to send will be saved to an intermediate buffer until after a CRLF is written, then the buffer will be flushed and sent through the socket. This intermediate buffer is a maximum of 512 bytes (The max size of input buffer as specified in the IRC RFC). The line that gets read from In function will be read until after a CRLF is detected, then a null byte will be appended to the specified input buffer.

main.cpp

#include <iostream>
#include <stdlib.h>
#include <signal.h>
#include "sock.h"
using namespace std;
void help(char *fname);
void quit(int sig);

plug s; /* <--- This is the socket */

int main(int argc, char** argv) {
    if (argc<5) help(argv[0]);

    /* Map CTRL-C to quit() */
    struct sigaction sa;
    sa.sa_handler = &quit;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT,&sa,0);

    char buf[513];
    int chanlen = strlen(argv[3]), nicklen = strlen(argv[4]);

    /* Connect to host (arg1) on port (arg2) */
    s.link(argv[1],atoi(argv[2]));

    /* The rest should be self-explanatory */
    s << "USER " << argv[4] << " 0 * :" << argv[4] << "\r\n";
    s << "NICK " << argv[4] << "\r\n";

    while (s.in(buf)) {
        if (strstr(buf,"PING ") == buf) {
            s << "PO" << buf+2;
        } else if (*buf==':' && !memcmp(strchr(buf,' ')+1,"001",3)) {
            s << "MODE " << argv[4] << " +B\r\n";
            s << "JOIN " << argv[3] << "\r\n";
        } else if (*buf==':' && !memcmp(strchr(buf,' ')+1,"PRIVMSG",7)) {
            if (!memcmp(strchr(buf,'#'),argv[3],chanlen) && !memcmp(strrchr(buf,':')+1,argv[4],nicklen)) {
                s << "PRIVMSG " << argv[3] << " :Someone just spoke to me!\r\n";
            }
        }
        cout << buf;
    }
    s.unlink();
    return 0;
}

void help(char *fname) {
    cout << "Usage: " << fname << " hostname port channel nick" << endl;
    exit(1);
}

void quit(int sig) {
    s << "QUIT :Someone pressed CTRL-C on me.\r\n";
    s.unlink();
    exit(0);
}

sock.h

#ifndef SOCK_H
#define   SOCK_H
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
class plug {
public:
    int fd;
    int link(const char* host, int port); /* Connect */
    int unlink(); /* Disconnect */
    int in(char* buf); /* Read line (plus added nul char) */
    int out(const char* buf); /* Write line */
    plug& operator <<(const char* buf);
    plug();
private:
    int bi;
    char buffer[512];
};

#endif

sock.cpp

#include "sock.h"


plug::plug() { bi = 0; }

int plug::link(const char* host, int port) {
    struct sockaddr_in si;
    struct hostent *hostess_twinkies = gethostbyname(host);
    fd = socket(PF_INET, SOCK_STREAM, 0);
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr = *((struct in_addr*)hostess_twinkies->h_addr);
    memset(&(si.sin_zero),'',8);
    if (connect(fd,(struct sockaddr*)&si,sizeof(struct sockaddr))==-1) fd=0;
    return fd;
}

int plug::unlink() { return close(fd); }

int plug::in(char* buf) {
    int len;
    for (len=0;recv(fd,buf,1,0);len++,buf++)
        if (*buf=='\n')
            if (*(buf-1)=='\r') {
                *(buf+1)='';
                return len;
            }
}
int plug::out(const char* buf) {
    int i;
    for (i=0;*(short*)buf!=0x0a0d;i++,buf++);
    buf-=i; i+=2;
    return send(fd,buf,i,0);
}

plug& plug::operator <<(const char* buf) {
    int len = strlen(buf);
    memmove(buffer+bi,buf,len);
    bi+=len;
    if (buf[len-1]=='\n' && buf[len-2]=='\r') {
        send(fd,buffer,bi,0);
        bi = 0;
    }
    return *this;
}

One thought on “Simple C++ IRC Bot for Linux

  1. You are the man! I was struggling with managing the receiving buffer and you’re solution got me going, looks elegant and simple!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: