Tsunami IRC Bot for Mac OS X. Supports easy addition of new command, changing of options, server information, and more.
6ac77bdbf13108f09a7a33d7b14e7d344a95cc50f60b085efc05daf3bacb3350
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <netdb.h>
#include <stdarg.h>
#include <unistd.h>
#define PASSWORD "password"
#define CMDPREFIX "."
#define IRCSERV "irc.prison.net"
#define IRCPORT 6667
#define IRCCHAN "#tsunami"
#define IRCPASS ""
#define BOTNICK "tsunami670"
int sock;
char logged_in[64];
int irc_send(char *Format, ...)
{
va_list va;
char buf[1024];
memset(buf,0,sizeof(buf));
va_start(va, Format);
vsprintf(buf, Format, va);
va_end(va);
printf("%s", buf);
return send(sock, buf, strlen(buf), 0);
}
char *host_addr(const char *addr)
{
/* performs host resolution, pass NULL to get local ip */
struct hostent *he = NULL;
char address[64];
if (addr == NULL)
strcpy(address, "");
else
strcpy(address, addr);
he = gethostbyname(address);
if(he == NULL)
return NULL;
return inet_ntoa(*(struct in_addr *) he->h_addr_list[0]);
}
////////////////////////////////////////////////////////////////////////
// Establishes a connection to the IRC server.
//
int irc_connect()
{
struct sockaddr_in addr;
addr.sin_addr.s_addr = inet_addr(host_addr(IRCSERV));
addr.sin_family = AF_INET;
addr.sin_port = htons(IRCPORT);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
return 0;
if (strlen(IRCPASS))
irc_send("PASS %s\r\n", IRCPASS);
irc_send("USER %s . . :tsunami\r\n", BOTNICK);
irc_send("NICK %s\r\n", BOTNICK);
return 1;
}
////////////////////////////////////////////////////////////////////////
// Parse data for \n into chunks to be processed for command
// processing.
//
int irc_read(char *buffer, size_t size)
{
char *result;
unsigned short len;
char msg_from[256], msg_type[16], msg_to[64], msg_data[512];
printf("%s", buffer);
/* parse multiple lines */
if ((result = strtok(buffer, "\n")) != NULL)
{
do
{
/* pong back to the server */
if (!strncasecmp(result, "PING ", 5))
{
result[1] = 'O';
irc_send("%s\n", result);
}
else
{
char *cr;
/* check for leading : then remove it and initialize msgd */
if (result[0] != ':')
return -1;
result++;
/* fill msgd.from with 1st parameter */
len = strstr(result, " ") - result;
if (len < 1 || len > sizeof(msg_from)-1)
return -1;
strncpy(msg_from, result, len);
result = result+len+1;
/* fill msgd.type with 2nd parameter and make it all uppercase */
len = strstr(result, " ") - result;
if (len < 1 || len > sizeof(msg_type)-1)
return -1;
strncpy(msg_type, result, len);
result = result+len+1;
for (len = 0; len < strlen(msg_type); len++)
msg_type[len] = toupper(msg_type[len]);
/* if there is a 3rd parameter then fill msgd.to with it */
if (result[0] != ':')
{
len = strstr(result, " ") - result;
if (len < 1 || len > sizeof(msg_to)-1)
return -1;
strncpy(msg_to, result, len);
result = result+len+1;
}
else
result++; /* strip leading : */
/* fill msgd.data with the remainder of the buffer */
if (result[0] == ':')
result++; /* strip leading : */
if (strlen(result))
strncpy(msg_data, result, sizeof(msg_data) - 1);
if ( (cr = strrchr(msg_data, '\r')) != NULL)
cr[0] = 0;
/* send msgd to be parsed further */
irc_parse(msg_from, msg_type, msg_to, msg_data);
}
}while((result = strtok(NULL, "\n")) != NULL);
}
}
int irc_parse(char *msg_from, char *msg_type, char *msg_to, char *msg_data)
{
long len;
char *buf = msg_data;
char nick[32], user[32], host[128], chan[64];
if (!strcmp(msg_type, "001"))
{
irc_send("JOIN %s\r\n", IRCCHAN);
return 0;
}
if (!strcmp(msg_type, "451"))
{
irc_send("USER %s . . :tsunami\r\n", BOTNICK);
irc_send("NICK %s\r\n", BOTNICK);
return 0;
}
///////////////////////////////////////////////////////////
// PARSED MESSAGE HANDLER //
///////////////////////////////////////////////////////////
/* parse through data to fill variables */
buf = msg_from;
if (strcmp(msg_type, "PRIVMSG"))
return 0;
if (!strlen(msg_data))
return 0;
/* extract the nick from the buffer and fill ui struct */
if ((len = strstr(buf, "!") - buf) < 1)
return 0;
if (len > (sizeof(nick) - 1))
len = sizeof(nick) - 1;
strncpy(nick, buf, len);
buf = buf+len+1;
/* extract the ident from the buffer and fill ui struct */
if ((len = strstr(buf, "@") - buf) < 1)
return 0;
if (len > (sizeof(user) - 1))
len = sizeof(user) - 1;
strncpy(user, buf, len);
buf = buf+len+1;
/* copy the remainder of the buffer into ui struct */
strncpy(host, buf, sizeof(host) - 1);
strncpy(chan, IRCCHAN, sizeof(chan) - 1);
irc_cmd(nick, user, host, chan, msg_data, msg_type);
return 0;
}
////////////////////////////////////////////////////////////////////////
// Main command parsing and processing function. Breaks the message
// down into parameters and dispatches them to their associated
// set of code for processing.
//
int irc_cmd(char *nick, char *user, char *host, char *chan, char *msg_data, char *msg_type)
{
/* handle command processing */
char ctcp_time[] = "\001TIME\001";
char ctcp_version[] = "\001VERSION\001";
char param[9][128];
char *buf = msg_data;
unsigned int k;
long len = 0;
/* VERSION */
if (!strncmp(buf, ctcp_version, sizeof(ctcp_version) - 1))
{
irc_send("NOTICE %s :\001VERSION tsunami\001\r\n", nick);
return 0;
}
/* TIME */
if (!strncmp(buf, ctcp_time, sizeof(ctcp_time) - 1))
{
struct tm *gmt;
time_t gtime;
time(>ime);
gmt = gmtime(>ime);
irc_send("NOTICE %s :\001TIME %s\001\r\n", nick, asctime(gmt));
return 0;
}
///////////////////////////////////////////////////////////
// INIT //
///////////////////////////////////////////////////////////
/* set variables to null and copy the message into the buffer for processing */
memset(¶m, 0, sizeof(param));
if ( strncmp(buf, CMDPREFIX, strlen(CMDPREFIX)) )
return 0;
/* copy the command and parameters into param[1-8] */
for (k = 0; k < (sizeof(param) / sizeof(param[0])); k++)
{
/* strip leading command prefix */
if (k == 0)
{
if ( !strncmp(buf, CMDPREFIX, strlen(CMDPREFIX)) )
buf+=strlen(CMDPREFIX);
}
/* length is the location of the first 20h minus the location of the start of string */
if (strstr(buf, " "))
len = strstr(buf, " ") - buf;
else
len = 0;
if (len < 1)
break;
if (len >= sizeof(param[0]))
strncpy(param[k], buf, len - sizeof(param[0]) - 1);
else
strncpy(param[k], buf, len);
buf = buf+len+1;
}
if (strlen(buf))
strncpy(param[k], buf, sizeof(param[k]));
/* .login <password> - request login using password */
if (!strcmp(param[0], "login"))
{
if (!strlen(param[1]))
return 0;
if (!strcmp(PASSWORD, param[1]))
{
strcpy(logged_in, PASSWORD);
irc_send("PRIVMSG %s :You have been logged in.\r\n", chan);
}
else
memset(&logged_in, 0, sizeof(logged_in));
return 0;
}
///////////////////////////////////////////////////////////
// LOGGED //
///////////////////////////////////////////////////////////
/* drop request if not logged in */
if (strcmp(PASSWORD, logged_in))
return 0;
/* .op <nick> */
if (!strcmp(param[0], "op"))
{
irc_send("MODE %s +o %s\r\n", chan, nick);
return 0;
}
return 0;
}
int main(int argc, char *argv[])
{
fd_set fds;
time_t tov_count;
double bot_tov = 0;
time_t sock_tov;
struct timeval tv;
/* initialize data values */
srand(time(NULL));
memset(&tv, 0, sizeof(tv));
tv.tv_sec = 1;
/* start main socket read loop */
while(1)
{
/* setup socket for select() model */
FD_ZERO(&fds);
FD_SET(sock, &fds);
select(0, &fds, NULL, NULL, &tv);
/* store time for timeout value and check it */
time(&tov_count);
bot_tov = difftime(tov_count, sock_tov);
/* bot timed out, reconnect: else check for process data packet */
if (bot_tov > 200)
{
close(sock);
irc_connect();
time(&sock_tov);
}
else if (FD_ISSET(sock, &fds))
{
int len;
char buffer[2048];
/* reset socket read time and initialize buffer */
memset(buffer, 0, sizeof(buffer));
len = recv(sock, buffer, sizeof(buffer)-1, 0);
/* error reading from socket: reconnect and reset socket read time */
if (len <= 0)
{
close(sock);
irc_connect();
time(&sock_tov);
}
time(&sock_tov);
/* send data for processing */
irc_read(buffer, strlen(buffer));
}
}
return 0;
}