diff options
author | Happy <markaizer@gmail.com> | 2014-08-21 04:50:46 +0800 |
---|---|---|
committer | Happy <markaizer@gmail.com> | 2014-08-21 04:50:46 +0800 |
commit | f52e1007fe08c67003c0bc4c78231904dd3fd5cc (patch) | |
tree | 99907d827264e501774e58ab4630e41fa7103c02 /src/map/irc-bot.c | |
parent | 2410110dece79b4598c12f1c953219f1d0d1904a (diff) | |
parent | 769b1d05aa5cfa8cddfe7d21b35d5c5e4da3bbd6 (diff) | |
download | hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.gz hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.bz2 hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.xz hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.zip |
Merge pull request #1 from HerculesWS/master
Update from original
Diffstat (limited to 'src/map/irc-bot.c')
-rw-r--r-- | src/map/irc-bot.c | 313 |
1 files changed, 255 insertions, 58 deletions
diff --git a/src/map/irc-bot.c b/src/map/irc-bot.c index 7f03ed8d4..6f016697f 100644 --- a/src/map/irc-bot.c +++ b/src/map/irc-bot.c @@ -2,26 +2,36 @@ // See the LICENSE file // Base Author: shennetsind @ http://hercules.ws +#define HERCULES_CORE + +#include "irc-bot.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "clif.h" +#include "map.h" +#include "pc.h" #include "../common/cbasetypes.h" #include "../common/malloc.h" -#include "../common/strlib.h" +#include "../common/random.h" #include "../common/showmsg.h" #include "../common/socket.h" +#include "../common/strlib.h" #include "../common/timer.h" -#include "../common/random.h" -#include "map.h" -#include "pc.h" -#include "clif.h" -#include "irc-bot.h" +//#define IRCBOT_DEBUG -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +struct irc_bot_interface irc_bot_s; -char send_string[200]; +char send_string[IRC_MESSAGE_LENGTH]; -int irc_connect_timer(int tid, unsigned int tick, int id, intptr_t data) { +/** + * Timer callback to (re-)connect to an IRC server + * @see timer->do_timer + */ +int irc_connect_timer(int tid, int64 tick, int id, intptr_t data) { struct hSockOpt opt; if( ircbot->isOn || ++ircbot->fails >= 3 ) return 0; @@ -29,18 +39,22 @@ int irc_connect_timer(int tid, unsigned int tick, int id, intptr_t data) { opt.silent = 1; opt.setTimeo = 0; - ircbot->last_try = iTimer->gettick(); + ircbot->last_try = timer->gettick(); if( ( ircbot->fd = make_connection(ircbot->ip,hChSys.irc_server_port,&opt) ) > 0 ){ session[ircbot->fd]->func_parse = ircbot->parse; session[ircbot->fd]->flag.server = 1; - iTimer->add_timer(iTimer->gettick() + 3000, ircbot->identify_timer, 0, 0); + timer->add(timer->gettick() + 3000, ircbot->identify_timer, 0, 0); ircbot->isOn = true; } return 0; } -int irc_identify_timer(int tid, unsigned int tick, int id, intptr_t data) { +/** + * Timer callback to send identification commands to an IRC server + * @see timer->do_timer + */ +int irc_identify_timer(int tid, int64 tick, int id, intptr_t data) { if( !ircbot->isOn ) return 0; @@ -49,18 +63,25 @@ int irc_identify_timer(int tid, unsigned int tick, int id, intptr_t data) { sprintf(send_string, "NICK %s", hChSys.irc_nick); ircbot->send(send_string); - iTimer->add_timer(iTimer->gettick() + 3000, ircbot->join_timer, 0, 0); + timer->add(timer->gettick() + 3000, ircbot->join_timer, 0, 0); return 0; } -int irc_join_timer(int tid, unsigned int tick, int id, intptr_t data) { +/** + * Timer callback to join channels (and optionally send NickServ commands) + * @see timer->do_timer + */ +int irc_join_timer(int tid, int64 tick, int id, intptr_t data) { if( !ircbot->isOn ) return 0; if( hChSys.irc_nick_pw[0] != '\0' ) { sprintf(send_string, "PRIVMSG NICKSERV : IDENTIFY %s", hChSys.irc_nick_pw); ircbot->send(send_string); + if( hChSys.irc_use_ghost ) { + sprintf(send_string, "PRIVMSG NICKSERV : GHOST %s %s", hChSys.irc_nick, hChSys.irc_nick_pw); + } } sprintf(send_string, "JOIN %s", hChSys.irc_channel); @@ -70,6 +91,12 @@ int irc_join_timer(int tid, unsigned int tick, int id, intptr_t data) { return 0; } +/** + * Search the handler for a given IRC received command + * @param function_name Name of the received IRC command + * @return Function pointer to the command handler, NULL in case + * of unhandled commands + */ struct irc_func* irc_func_search(char* function_name) { int i; for(i = 0; i < ircbot->funcs.size; i++) { @@ -80,6 +107,10 @@ struct irc_func* irc_func_search(char* function_name) { return NULL; } +/** + * Parser for the IRC server connection + * @see do_sockets + */ int irc_parse(int fd) { char *parse_string = NULL, *str_safe = NULL; @@ -90,7 +121,7 @@ int irc_parse(int fd) { ircbot->isIn = false; ircbot->fails = 0; ircbot->ip = host2ip(hChSys.irc_server); - iTimer->add_timer(iTimer->gettick() + 120000, ircbot->connect_timer, 0, 0); + timer->add(timer->gettick() + 120000, ircbot->connect_timer, 0, 0); return 0; } @@ -112,83 +143,156 @@ int irc_parse(int fd) { return 0; } +/** + * Parse the source from a received irc message + * @param source Source string, as reported by the server + * @param nick Pointer to a string where to return the nick (may not be NULL, + * needs to be able to fit an IRC_NICK_LENGTH long string) + * @param ident Pointer to a string where to return the ident (may not be + * NULL, needs to be able to fit an IRC_IDENT_LENGTH long string) + * @param host Pointer to a string where to return the hostname (may not be + * NULL, needs to be able to fit an IRC_HOST_LENGTH long string) + */ void irc_parse_source(char *source, char *nick, char *ident, char *host) { - int i, len = strlen(source), pos = 0; + int i, pos = 0; + size_t len = strlen(source); unsigned char stage = 0; for(i = 0; i < len; i++) { if( stage == 0 && source[i] == '!' ) { - memcpy(nick, &source[0], min(i,IRC_NICK_LENGTH)); - nick[i] = '\0'; + safestrncpy(nick, &source[0], min(i + 1, IRC_NICK_LENGTH)); pos = i+1; stage = 1; } else if( stage == 1 && source[i] == '@' ) { - memcpy(ident, &source[pos], min(i - pos,IRC_IDENT_LENGTH)); - ident[i-pos] = '\0'; - memcpy(host, &source[i+1], min(len - i,IRC_HOST_LENGTH)); - host[len] = '\0'; + safestrncpy(ident, &source[pos], min(i - pos + 1, IRC_IDENT_LENGTH)); + safestrncpy(host, &source[i+1], min(len - i, IRC_HOST_LENGTH)); break; } } } + +/** + * Parse a received message from the irc server, and do the appropriate action + * for the detected command + * @param fd IRC server connection file descriptor + * @param str Raw received message + */ void irc_parse_sub(int fd, char *str) { - char source[180], command[60], target[60], message[200]; + char source[180], command[60], buf1[IRC_MESSAGE_LENGTH], buf2[IRC_MESSAGE_LENGTH]; + char *target = buf1, *message = buf2; struct irc_func *func; - source[0] = command[0] = target[0] = message[0] = '\0'; + source[0] = command[0] = buf1[0] = buf2[0] = '\0'; if( str[0] == ':' ) str++; - sscanf(str, "%179s %59s %59s :%199[^\r\n]", source, command, target, message); + if (sscanf(str, "%179s %59s %499s :%499[^\r\n]", source, command, buf1, buf2) == 3 && buf1[0] == ':') { + // source command :message (i.e. QUIT) + message = buf1+1; + target = buf2; + } if( command[0] == '\0' ) return; if( !(func = ircbot->func_search(command)) && !(func = ircbot->func_search(source)) ) { - //ShowWarning("Unknown command received %s from %s\n",command,source); +#ifdef IRCBOT_DEBUG + ShowWarning("Unknown command received %s from %s\n",command,source); +#endif // IRCBOT_DEBUG return; } func->func(fd,command,source,target,message); } +/** + * Send a raw command to the irc server + * @param str Command to send + */ void irc_send(char *str) { - int len = strlen(str) + 2; + size_t len = strlen(str) + 2; + if (len > IRC_MESSAGE_LENGTH-3) + len = IRC_MESSAGE_LENGTH-3; WFIFOHEAD(ircbot->fd, len); - snprintf((char*)WFIFOP(ircbot->fd,0),200, "%s\r\n", str); + snprintf((char*)WFIFOP(ircbot->fd,0),IRC_MESSAGE_LENGTH, "%s\r\n", str); WFIFOSET(ircbot->fd, len); } +/** + * Handler for the PING IRC command (send back a PONG) + * @see irc_parse_sub + */ void irc_pong(int fd, char *cmd, char *source, char *target, char *msg) { sprintf(send_string, "PONG %s", cmd); ircbot->send(send_string); } -void irc_join(int fd, char *cmd, char *source, char *target, char *msg) { - if( ircbot->isIn ) - return; - sprintf(send_string, "JOIN %s", hChSys.irc_channel); - ircbot->send(send_string); - ircbot->isIn = true; +/** + * Handler for CTCP commands received via PRIVMSG + * @see irc_privmsg + */ +void irc_privmsg_ctcp(int fd, char *cmd, char *source, char *target, char *msg) { + char source_nick[IRC_NICK_LENGTH], source_ident[IRC_IDENT_LENGTH], source_host[IRC_HOST_LENGTH]; + + source_nick[0] = source_ident[0] = source_host[0] = '\0'; + + if( source[0] != '\0' ) + ircbot->parse_source(source,source_nick,source_ident,source_host); + + if( strcmpi(cmd,"ACTION") == 0 ) { + if( ircbot->channel ) { + snprintf(send_string, 150, "[ #%s ] * IRC.%s %s *",ircbot->channel->name,source_nick,msg); + clif->chsys_msg2(ircbot->channel,send_string); + } + } else if( strcmpi(cmd,"ERRMSG") == 0 ) { + // Ignore it + } else if( strcmpi(cmd,"FINGER") == 0 ) { + // Ignore it + } else if( strcmpi(cmd,"PING") == 0 ) { + sprintf(send_string, "NOTICE %s :\001PING %s\001",source_nick,msg); + ircbot->send(send_string); + } else if( strcmpi(cmd,"TIME") == 0 ) { + time_t time_server; // variable for number of seconds (used with time() function) + struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... + char temp[CHAT_SIZE_MAX]; + + memset(temp, '\0', sizeof(temp)); + + time(&time_server); // get time in seconds since 1/1/1970 + datetime = localtime(&time_server); // convert seconds in structure + // like sprintf, but only for date/time (Sunday, November 02 2003 15:12:52) + strftime(temp, sizeof(temp)-1, msg_txt(230), datetime); // Server time (normal time): %A, %B %d %Y %X. + + sprintf(send_string, "NOTICE %s :\001TIME %s\001",source_nick,temp); + ircbot->send(send_string); + } else if( strcmpi(cmd,"VERSION") == 0 ) { + sprintf(send_string, "NOTICE %s :\001VERSION Hercules.ws IRC Bridge\001",source_nick); + ircbot->send(send_string); +#ifdef IRCBOT_DEBUG + } else { + ShowWarning("Unknown CTCP command received %s (%s) from %s\n",cmd,msg,source); +#endif // IRCBOT_DEBUG + } } +/** + * Handler for the PRIVMSG IRC command (action depends on the message contents) + * @see irc_parse_sub + */ void irc_privmsg(int fd, char *cmd, char *source, char *target, char *msg) { - if( strcmpi(target,hChSys.irc_nick) == 0 ) { - if( msg[0] == ':' ) msg++; - if( strcmpi(msg,"VERSION") == 0 ) { - char source_nick[IRC_NICK_LENGTH], source_ident[IRC_IDENT_LENGTH], source_host[IRC_HOST_LENGTH]; - - source_nick[0] = source_ident[0] = source_host[0] = '\0'; - - if( source[0] != '\0' ) - ircbot->parse_source(source,source_nick,source_ident,source_host); - - sprintf(send_string, "NOTICE %s :Hercules.ws IRC Bridge",source_nick); - ircbot->send(send_string); - return; - } - } else if( strcmpi(target,hChSys.irc_channel) == 0 ) { + if( msg && *msg == '\001' && strlen(msg) > 2 && msg[strlen(msg)-1] == '\001' ) { + // CTCP + char command[IRC_MESSAGE_LENGTH], message[IRC_MESSAGE_LENGTH]; + command[0] = message[0] = '\0'; + sscanf(msg, "\001%499[^\001\r\n ] %499[^\r\n\001]\001", command, message); + + irc_privmsg_ctcp(fd, command, source, target, message); +#ifdef IRCBOT_DEBUG + } else if( strcmpi(target,hChSys.irc_nick) == 0 ) { + ShowDebug("irc_privmsg: Received message from %s: '%s'\n", source ? source : "(null)", msg); +#endif // IRCBOT_DEBUG + } else if( msg && strcmpi(target,hChSys.irc_channel) == 0 ) { char source_nick[IRC_NICK_LENGTH], source_ident[IRC_IDENT_LENGTH], source_host[IRC_HOST_LENGTH]; source_nick[0] = source_ident[0] = source_host[0] = '\0'; @@ -197,26 +301,110 @@ void irc_privmsg(int fd, char *cmd, char *source, char *target, char *msg) { ircbot->parse_source(source,source_nick,source_ident,source_host); if( ircbot->channel ) { - snprintf(send_string, 150, "[ #%s ] IRC.%s : %s",ircbot->channel->name,source_nick,msg); - clif->chsys_msg2(ircbot->channel,send_string); + size_t padding_len = strlen(ircbot->channel->name) + strlen(source_nick) + 13; + while (1) { + snprintf(send_string, 150, "[ #%s ] IRC.%s : %s",ircbot->channel->name,source_nick,msg); + clif->chsys_msg2(ircbot->channel,send_string); + //break; // Uncomment this line to truncate long messages instead of posting them as multiple lines + if (strlen(msg) <= 149 - padding_len) + break; + msg += 149 - padding_len; + } } } } -void irc_relay (char *name, char *msg) { +/** + * Handler for the JOIN IRC command (notify an in-game channel of users joining + * the IRC channel) + * @see irc_parse_sub + */ +void irc_userjoin(int fd, char *cmd, char *source, char *target, char *msg) { + char source_nick[IRC_NICK_LENGTH], source_ident[IRC_IDENT_LENGTH], source_host[IRC_HOST_LENGTH]; + + source_nick[0] = source_ident[0] = source_host[0] = '\0'; + + if( source[0] != '\0' ) + ircbot->parse_source(source,source_nick,source_ident,source_host); + + if( ircbot->channel ) { + snprintf(send_string, 150, "[ #%s ] User IRC.%s joined the channel.",ircbot->channel->name,source_nick); + clif->chsys_msg2(ircbot->channel,send_string); + } +} + +/** + * Handler for the PART and QUIT IRC commands (notify an in-game channel of + * users leaving the IRC channel) + * @see irc_parse_sub + */ +void irc_userleave(int fd, char *cmd, char *source, char *target, char *msg) { + char source_nick[IRC_NICK_LENGTH], source_ident[IRC_IDENT_LENGTH], source_host[IRC_HOST_LENGTH]; + + source_nick[0] = source_ident[0] = source_host[0] = '\0'; + + if( source[0] != '\0' ) + ircbot->parse_source(source,source_nick,source_ident,source_host); + + if( ircbot->channel ) { + if (!strcmpi(cmd, "QUIT")) + snprintf(send_string, 150, "[ #%s ] User IRC.%s left the channel. [Quit: %s]",ircbot->channel->name,source_nick,msg); + else + snprintf(send_string, 150, "[ #%s ] User IRC.%s left the channel. [%s]",ircbot->channel->name,source_nick,msg); + clif->chsys_msg2(ircbot->channel,send_string); + } +} + +/** + * Handler for the NICK IRC commands (notify an in-game channel of users + * changing their name while in the IRC channel) + * @see irc_parse_sub + */ +void irc_usernick(int fd, char *cmd, char *source, char *target, char *msg) { + char source_nick[IRC_NICK_LENGTH], source_ident[IRC_IDENT_LENGTH], source_host[IRC_HOST_LENGTH]; + + source_nick[0] = source_ident[0] = source_host[0] = '\0'; + + if( source[0] != '\0' ) + ircbot->parse_source(source,source_nick,source_ident,source_host); + + if( ircbot->channel ) { + snprintf(send_string, 150, "[ #%s ] User IRC.%s is now known as IRC.%s",ircbot->channel->name,source_nick,msg); + clif->chsys_msg2(ircbot->channel,send_string); + } +} + +/** + * Relay a chat message to the irc channel the bot is connected to + * @param name Sender's name + * @param msg Message text + */ +void irc_relay(char *name, const char *msg) { if( !ircbot->isIn ) return; sprintf(send_string,"PRIVMSG %s :[ %s ] : %s",hChSys.irc_channel,name,msg); ircbot->send(send_string); } -void irc_bot_init(void) { + +/** + * IRC bot initializer + */ +void irc_bot_init(bool minimal) { + /// Command handlers const struct irc_func irc_func_base[] = { { "PING" , ircbot->pong }, { "PRIVMSG", ircbot->privmsg }, + { "JOIN", ircbot->userjoin }, + { "QUIT", ircbot->userleave }, + { "PART", ircbot->userleave }, + { "NICK", ircbot->usernick }, }; struct irc_func* function; int i; + if (minimal) + return; + if( !hChSys.irc ) return; @@ -245,10 +433,13 @@ void irc_bot_init(void) { ircbot->isIn = false; ircbot->isOn = false; - iTimer->add_timer_func_list(ircbot->connect_timer, "irc_connect_timer"); - iTimer->add_timer(iTimer->gettick() + 7000, ircbot->connect_timer, 0, 0); + timer->add_func_list(ircbot->connect_timer, "irc_connect_timer"); + timer->add(timer->gettick() + 7000, ircbot->connect_timer, 0, 0); } +/** + * IRC bot finalizer + */ void irc_bot_final(void) { int i; @@ -265,6 +456,9 @@ void irc_bot_final(void) { aFree(ircbot->funcs.list); } +/** + * IRC bot interface defaults initializer + */ void ircbot_defaults(void) { ircbot = &irc_bot_s; @@ -287,6 +481,9 @@ void ircbot_defaults(void) { ircbot->relay = irc_relay; ircbot->pong = irc_pong; - ircbot->join = irc_join; ircbot->privmsg = irc_privmsg; + + ircbot->userjoin = irc_userjoin; + ircbot->userleave = irc_userleave; + ircbot->usernick = irc_usernick; } |