void loop(char *nick, char *host) { fd_set rfs, wfs; FILE *fs, *fb; int hasline; int m; fs = fdopen(sserve, "r+"); fb = fdopen(sbroad, "r+"); # define max(a,b) ((a) > (b) ? (a) : (b)) m = max(sserve, sbroad); snprintf(wsay.b, sizeof(wsay.b)-2, "!iam %s\n", nick); hasline = 1; drawbar("connected as %s@%s\n", nick, host); for (;;) { fdset(&rfs, &wfs); if (select(m+1, &rfs, &wfs, NULL, NULL) == -1) { perror("select"); return; } if (FD_ISSET(sserve, &wfs)) if (hasline) { hasline = 0; if (say() < 0) return; redraw(); } if (FD_ISSET(0, &rfs)) hasline = peek(), redraw(); if (FD_ISSET(sserve, &rfs)) hear(sserve), redraw(); if (FD_ISSET(sbroad, &rfs)) hear(sbroad), redraw(); } }
int main(int argc, char *argv[]) { char *conf; char *serverloc="irc.freenode.net", *portno="6667", *nick="VirgilBot", *chan="#sii", *netf="net"; // defaults int awkw=30; char pref='!'; float awkp=0.9; bool slnt=false; int arg; for(arg=1;arg<argc;arg++) { if(strncmp(argv[arg], "--serv=", 7)==0) { serverloc=argv[arg]+7; } else if(strncmp(argv[arg], "--port=", 7)==0) { portno=argv[arg]+7; } else if(strncmp(argv[arg], "--nick=", 7)==0) { nick=argv[arg]+7; } else if(strncmp(argv[arg], "--chan=", 7)==0) { chan=argv[arg]+7; } else if(strncmp(argv[arg], "--netf=", 7)==0) { netf=argv[arg]+7; } else if(strncmp(argv[arg], "--awkw=", 7)==0) { sscanf(argv[arg]+7, "%d", &awkw); } else if(strncmp(argv[arg], "--awkp=", 7)==0) { sscanf(argv[arg]+7, "%g", &awkp); } else if(strncmp(argv[arg], "--pref=", 7)==0) { pref=argv[arg][7]; } else if(strcmp(argv[arg], "--slnt")==0) { slnt=true; } else if(strcmp(argv[arg], "--no-slnt")==0) { slnt=false; } else if(strncmp(argv[arg], "--conf=", 7)==0) { conf=argv[arg]+7; FILE *fconf=fopen(conf, "r"); if(!fconf) { printf("bot: failed to open file \"%s\"\n", conf); perror("bot: fopen"); } else { char *line; while((line=fgetl(fconf))) { if(strncmp(line, "SERV ", 5)==0) { serverloc=strdup(line+5); } else if(strncmp(line, "PORT ", 5)==0) { portno=strdup(line+5); } else if(strncmp(line, "NICK ", 5)==0) { nick=strdup(line+5); } else if(strncmp(line, "CHAN ", 5)==0) { chan=strdup(line+5); } else if(strncmp(line, "NETF ", 5)==0) { netf=strdup(line+5); } else if(strncmp(line, "AWKW ", 5)==0) { sscanf(line+5, "%d", &awkw); } else if(strncmp(line, "AWKP ", 5)==0) { sscanf(line+5, "%g", &awkp); } else if(strncmp(line, "PREF ", 5)==0) { pref=line[5]; } else if(strncmp(line, "SLNT ", 5)==0) { slnt=(line[5]=='+'); } free(line); } } } } int wp[2],rp[2],e; int ww,rr; if((e=pipe(wp))) { fprintf(stderr, "bot: Error: Failed to create pipe: %s\n", strerror(errno)); return(3); } if((e=pipe(rp))) { fprintf(stderr, "bot: Error: Failed to create pipe: %s\n", strerror(errno)); return(3); } int pid=fork(); switch(pid) { case -1: // failure fprintf(stderr, "bot: Error: failed to fork virgil: %s\n", strerror(errno)); break; case 0: // child { ww=rp[1];close(rp[0]); rr=wp[0];close(wp[1]); if((e=dup2(ww, STDERR_FILENO))==-1) { fprintf(stderr, "bot.child: Error: Failed to redirect stderr with dup2: %s\n", strerror(errno)); return(3); } if((e=dup2(rr, STDIN_FILENO))==-1) { fprintf(stderr, "bot.child: Error: Failed to redirect stdin with dup2: %s\n", strerror(errno)); return(3); } execl("./virgil", "virgil", "-p", netf, NULL); fprintf(stderr, "bot.child: Error: Failed to execl virgil: %s\n", strerror(errno)); return(255); } break; default: // parent ww=wp[1];close(wp[0]); rr=rp[0];close(rp[1]); break; } FILE *fchild = fdopen(ww, "w"); // get a stream to send stuff to the child if(!fchild) { fprintf(stderr, "bot: Error: Failed put stream on write-pipe: %s\n", strerror(errno)); return(3); } fprintf(fchild, "L\n"); fflush(fchild); int serverhandle; struct addrinfo hints, *servinfo; printf("bot: Looking up server: %s:%s\n", serverloc, portno); memset(&hints, 0, sizeof(hints)); hints.ai_family=AF_INET; hints.ai_socktype = SOCK_STREAM; // TCP stream sockets int rv; if((rv = getaddrinfo(serverloc, portno, &hints, &servinfo)) != 0) { fprintf(stderr, "bot: getaddrinfo: %s\n", gai_strerror(rv)); return(1); } char sip[INET_ADDRSTRLEN]; printf("bot: running, connecting to server...\n"); struct addrinfo *p; // loop through all the results and connect to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { inet_ntop(p->ai_family, &(((struct sockaddr_in*)p->ai_addr)->sin_addr), sip, sizeof(sip)); printf("bot: connecting to %s\n", sip); if((serverhandle = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("bot: socket"); continue; } if(connect(serverhandle, p->ai_addr, p->ai_addrlen) == -1) { close(serverhandle); perror("bot: connect"); continue; } break; } if (p == NULL) { fprintf(stderr, "bot: failed to connect\n"); return(1); } freeaddrinfo(servinfo); // all done with this structure char *nickmsg=(char *)malloc(6+strlen(nick)); sprintf(nickmsg, "NICK %s", nick); tx(serverhandle, nickmsg); free(nickmsg); tx(serverhandle, "USER virgil dev-null newnet :Virgil AI Chatbot"); fd_set master, readfds; FD_ZERO(&master); FD_SET(fileno(stdin), &master); FD_SET(serverhandle, &master); int errupt=0; char *packet=NULL; char *qmsg=NULL; int state=0; while(!errupt) { struct timeval timeout; timeout.tv_sec=awkw; timeout.tv_usec=0; readfds=master; if(select(serverhandle+1, &readfds, NULL, NULL, &timeout) == -1) { perror("bot: select"); return(2); } if(FD_ISSET(fileno(stdin), &readfds)) { getchar(); errupt++; // for now, input just means die qmsg="virgil bot shut down"; } if(FD_ISSET(serverhandle, &readfds)) { int e; if((e=rx(serverhandle, &packet))!=0) { fprintf(stderr, "bot: error: rx(%d, &%p): %d\n", serverhandle, packet, e); errupt++; qmsg="error: virgil crashed"; } else if(packet[0]!=0) { char *p=packet; printf("bot: rx: %s\n", p); if(p[0]==':') { p=strchr(p, ' '); } char *cmd=strtok(p, " "); if(strcmp(cmd, "PING")==0) { char *sender=strtok(NULL, " "); char *pong=(char *)malloc(15+strlen(sender)); sprintf(pong, "PONG virgil %s", sender+1); tx(serverhandle, pong); free(pong); } else if(strcmp(cmd, "MODE")==0) { if(state==0) { state=1; char *joinmsg=(char *)malloc(6+strlen(chan)); sprintf(joinmsg, "JOIN %s", chan); tx(serverhandle, joinmsg); free(joinmsg); } } else if(strcmp(cmd, "PRIVMSG")==0) { if(state==1) // lots of non-error-checked code here { char *dest=strtok(NULL, " "); char *msg=dest+strlen(dest)+1; // prefixed with : char *src=packet+1; char *bang=strchr(src, pref); if(bang) *bang=0; if(strcmp(dest, chan)==0) // to channel; check for ! { bool skip=false; bool notme=(msg[1]!=pref) || slnt; bool noans=false; if(strchr("LSX", msg[2]) && !msg[3]) { if(strcmp(src, "soundandfury")!=0) // checking on nick is a bad way to do it, though { skip=true; } noans=true; } if(strstr(msg, "://")) // block URLs, they confuse the poor thing skip=true; if(!skip && (strlen(msg)>2)) { if(notme) fprintf(fchild, "L\n\t%s\nS\n", msg+1); else fprintf(fchild, "L\n%s\nS\n", msg+2); fflush(fchild); if(!(noans || notme)) { char *vsay=NULL; hear(rr, &vsay); off_t o; for(o=2;o<strlen(vsay);o++) { vsay[o]=tolower(vsay[o]); } fprintf(stderr, "%s", vsay); char *resp=(char *)malloc(64+strlen(chan)+strlen(src)+(vsay==NULL?6:strlen(vsay))); sprintf(resp, "PRIVMSG %s :%s:%s", chan, src, vsay?vsay:"Error"); tx(serverhandle, resp); free(resp); if(vsay) free(vsay); } } } } } free(packet); } } else if((rand()>(RAND_MAX*awkp)) && !slnt) { fprintf(fchild, "L\nWell?\nS\n"); fflush(fchild); char *vsay=NULL; hear(rr, &vsay); off_t o; for(o=2;o<strlen(vsay);o++) { vsay[o]=tolower(vsay[o]); } fprintf(stderr, "%s", vsay); char *resp=(char *)malloc(64+strlen(chan)+(vsay==NULL?6:strlen(vsay))); sprintf(resp, "PRIVMSG %s :%s", chan, vsay?vsay:"Error"); tx(serverhandle, resp); free(resp); if(vsay) free(vsay); } } printf("bot: closing\n"); fprintf(fchild, "X\n"); fflush(fchild); fclose(fchild); if(!qmsg) qmsg="Quit."; char *quit = (char *)malloc(7+strlen(qmsg)); sprintf(quit, "QUIT %s", qmsg); tx(serverhandle, quit); free(quit); close(serverhandle); printf("bot: closed\n"); return(0); }