From 5edc982e3e2e0c6fc643b229d1c06ea75a618d15 Mon Sep 17 00:00:00 2001 From: hemagx Date: Sat, 23 Apr 2016 21:37:16 +0200 Subject: Added a flood protection to IRC Bot Now it's possible to send as many messages as possible without get kicked for flood. --- src/map/channel.c | 26 ++++++++++++- src/map/irc-bot.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/map/irc-bot.h | 18 ++++++++- 3 files changed, 144 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/map/channel.c b/src/map/channel.c index e4e3d069d..f36388bca 100644 --- a/src/map/channel.c +++ b/src/map/channel.c @@ -609,7 +609,10 @@ void read_channels_config(void) local_autojoin = 0, ally_autojoin = 0, allow_user_channel_creation = 0, irc_enabled = 0, - irc_autojoin = 0; + irc_autojoin = 0, + irc_flood_protection_rate = 0, + irc_flood_protection_burst = 0, + irc_flood_protection_enabled = 0; if( !libconfig->setting_lookup_string(settings, "map_local_channel_name", &local_name) ) local_name = "map"; @@ -686,7 +689,6 @@ void read_channels_config(void) libconfig->setting_lookup_bool(settings, "map_local_channel_autojoin", &local_autojoin); libconfig->setting_lookup_bool(settings, "ally_channel_autojoin", &ally_autojoin); libconfig->setting_lookup_bool(settings, "irc_channel_autojoin", &irc_autojoin); - if (local_autojoin) channel->config->local_autojoin = true; if (ally_autojoin) @@ -694,6 +696,26 @@ void read_channels_config(void) if (irc_autojoin) channel->config->irc_autojoin = true; + libconfig->setting_lookup_int(settings, "irc_flood_protection_burst", &irc_flood_protection_burst); + + if (irc_flood_protection_enabled) { + ircbot->flood_protection_enabled = true; + + libconfig->setting_lookup_bool(settings, "irc_flood_protection_enabled", &irc_flood_protection_enabled); + libconfig->setting_lookup_int(settings, "irc_flood_protection_rate", &irc_flood_protection_rate); + + if (irc_flood_protection_rate > 0) + ircbot->flood_protection_rate = irc_flood_protection_rate; + else + ircbot->flood_protection_rate = 1000; + if (irc_flood_protection_burst > 0) + ircbot->flood_protection_burst = irc_flood_protection_burst; + else + ircbot->flood_protection_burst = 3; + } else { + ircbot->flood_protection_enabled = false; + } + libconfig->setting_lookup_bool(settings, "allow_user_channel_creation", &allow_user_channel_creation); if( allow_user_channel_creation ) diff --git a/src/map/irc-bot.c b/src/map/irc-bot.c index b520e9e91..f1f76a616 100644 --- a/src/map/irc-bot.c +++ b/src/map/irc-bot.c @@ -80,9 +80,9 @@ int irc_identify_timer(int tid, int64 tick, int id, intptr_t data) { return 0; sprintf(send_string, "USER HerculesWS%d 8 * : Hercules IRC Bridge",rnd()%777); - ircbot->send(send_string); + ircbot->send(send_string, true); sprintf(send_string, "NICK %s", channel->config->irc_nick); - ircbot->send(send_string); + ircbot->send(send_string, true); timer->add(timer->gettick() + 3000, ircbot->join_timer, 0, 0); @@ -99,14 +99,15 @@ int irc_join_timer(int tid, int64 tick, int id, intptr_t data) { if (channel->config->irc_nick_pw[0] != '\0') { sprintf(send_string, "PRIVMSG NICKSERV : IDENTIFY %s", channel->config->irc_nick_pw); - ircbot->send(send_string); + ircbot->send(send_string, true); if (channel->config->irc_use_ghost) { sprintf(send_string, "PRIVMSG NICKSERV : GHOST %s %s", channel->config->irc_nick, channel->config->irc_nick_pw); + ircbot->send(send_string, true); } } sprintf(send_string, "JOIN %s", channel->config->irc_channel); - ircbot->send(send_string); + ircbot->send(send_string, true); ircbot->isIn = true; return 0; @@ -234,16 +235,85 @@ void irc_parse_sub(int fd, char *str) { func->func(fd,command,source,target,message); } +/** + * Decides if an IRC Command should be queued or not, based on the flood protection settings. + * + * @param str Command to be checked + **/ +void irc_queue(char *str) +{ + struct message_flood *queue_entry = NULL; + + if (!ircbot->flood_protection_enabled) { + ircbot->send(str, true); + return; + } + + if (ircbot->message_current == NULL) { + // No queue yet + if (ircbot->messages_burst_count < ircbot->flood_protection_burst) { + ircbot->send(str, true); + if (DIFF_TICK(timer->gettick(), ircbot->last_message_tick) <= ircbot->flood_protection_rate) + ircbot->messages_burst_count++; + else + ircbot->messages_burst_count = 0; + ircbot->last_message_tick = timer->gettick(); + } else { //queue starts + CREATE(queue_entry, struct message_flood, 1); + safestrncpy(queue_entry->message, str, sizeof(queue_entry->message)); + queue_entry->next = NULL; + ircbot->message_current = queue_entry; + ircbot->message_last = queue_entry; + ircbot->queue_tid = timer->add(timer->gettick() + ircbot->flood_protection_rate, ircbot->queue_timer, 0, 0); //start queue timer + ircbot->messages_burst_count = 0; + } + } else { + CREATE(queue_entry, struct message_flood, 1); + safestrncpy(queue_entry->message, str, sizeof(queue_entry->message)); + queue_entry->next = NULL; + ircbot->message_last->next = queue_entry; + ircbot->message_last = queue_entry; + } +} + +int irc_queue_timer(int tid, int64 tick, int id, intptr_t data) +{ + struct message_flood *queue_entry = ircbot->message_current; + nullpo_ret(queue_entry); + + ircbot->send(queue_entry->message, true); + if (queue_entry->next != NULL) { + ircbot->message_current = queue_entry->next; + ircbot->queue_tid = timer->add(timer->gettick() + ircbot->flood_protection_rate, ircbot->queue_timer, 0, 0); + } else { + ircbot->message_current = NULL; + ircbot->message_last = NULL; + ircbot->queue_tid = INVALID_TIMER; + } + + aFree(queue_entry); + + return 0; +} + /** * Send a raw command to the irc server * @param str Command to send */ -void irc_send(char *str) { +void irc_send(char *str, bool force) +{ size_t len; nullpo_retv(str); len = strlen(str) + 2; if (len > IRC_MESSAGE_LENGTH-3) len = IRC_MESSAGE_LENGTH-3; + + if (!force && ircbot->flood_protection_enabled) { + // Add to queue + ircbot->queue(str); + return; + } + WFIFOHEAD(ircbot->fd, len); snprintf(WFIFOP(ircbot->fd,0),IRC_MESSAGE_LENGTH, "%s\r\n", str); WFIFOSET(ircbot->fd, len); @@ -256,7 +326,7 @@ void irc_send(char *str) { void irc_pong(int fd, char *cmd, char *source, char *target, char *msg) { nullpo_retv(cmd); snprintf(send_string, IRC_MESSAGE_LENGTH, "PONG %s", cmd); - ircbot->send(send_string); + ircbot->send(send_string, false); } /** @@ -283,7 +353,7 @@ void irc_privmsg_ctcp(int fd, char *cmd, char *source, char *target, char *msg) // Ignore it } else if( strcmpi(cmd,"PING") == 0 ) { snprintf(send_string, IRC_MESSAGE_LENGTH, "NOTICE %s :\001PING %s\001",source_nick,msg); - ircbot->send(send_string); + ircbot->send(send_string, false); } 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, ... @@ -297,10 +367,10 @@ void irc_privmsg_ctcp(int fd, char *cmd, char *source, char *target, char *msg) strftime(temp, sizeof(temp)-1, msg_txt(230), datetime); // Server time (normal time): %A, %B %d %Y %X. snprintf(send_string, IRC_MESSAGE_LENGTH, "NOTICE %s :\001TIME %s\001",source_nick,temp); - ircbot->send(send_string); + ircbot->send(send_string, false); } else if( strcmpi(cmd,"VERSION") == 0 ) { snprintf(send_string, IRC_MESSAGE_LENGTH, "NOTICE %s :\001VERSION Hercules.ws IRC Bridge\001",source_nick); - ircbot->send(send_string); + ircbot->send(send_string, false); #ifdef IRCBOT_DEBUG } else { ShowWarning("Unknown CTCP command received %s (%s) from %s\n",cmd,msg,source); @@ -428,7 +498,7 @@ void irc_relay(const char *name, const char *msg) else sprintf(send_string,"PRIVMSG %s :%s", channel->config->irc_channel, msg); - ircbot->send(send_string); + ircbot->send(send_string, false); } /** @@ -479,6 +549,8 @@ void irc_bot_init(bool minimal) { ircbot->isOn = false; timer->add_func_list(ircbot->connect_timer, "irc_connect_timer"); + timer->add_func_list(ircbot->queue_timer, "irc_queue_timer"); + timer->add(timer->gettick() + 7000, ircbot->connect_timer, 0, 0); } @@ -491,10 +563,19 @@ void irc_bot_final(void) { if (!channel->config->irc) return; if( ircbot->isOn ) { - ircbot->send("QUIT :Hercules is shutting down"); + ircbot->send("QUIT :Hercules is shutting down", true); sockt->close(ircbot->fd); } + if (ircbot->queue_tid != INVALID_TIMER) + timer->delete(ircbot->queue_tid, ircbot->queue_timer); + + while (ircbot->message_current != NULL) { + struct message_flood *next = ircbot->message_current->next; + aFree(ircbot->message_current); + ircbot->message_current = next; + } + for( i = 0; i < ircbot->funcs.size; i++ ) { aFree(ircbot->funcs.list[i]); } @@ -509,6 +590,15 @@ void ircbot_defaults(void) { ircbot->channel = NULL; + ircbot->flood_protection_enabled = true; + ircbot->flood_protection_rate = 1000; + ircbot->flood_protection_burst = 3; + ircbot->last_message_tick = INVALID_TIMER; + ircbot->queue_tid = INVALID_TIMER; + ircbot->messages_burst_count = 0; + ircbot->message_current = NULL; + ircbot->message_last = NULL; + ircbot->init = irc_bot_init; ircbot->final = irc_bot_final; @@ -522,6 +612,8 @@ void ircbot_defaults(void) { ircbot->identify_timer = irc_identify_timer; ircbot->join_timer = irc_join_timer; + ircbot->queue_timer = irc_queue_timer; + ircbot->queue = irc_queue; ircbot->send = irc_send; ircbot->relay = irc_relay; diff --git a/src/map/irc-bot.h b/src/map/irc-bot.h index dc67e2fe8..949305bc9 100644 --- a/src/map/irc-bot.h +++ b/src/map/irc-bot.h @@ -39,6 +39,11 @@ struct irc_func { void (*func)(int, char*, char*, char*, char*); }; +struct message_flood { + char message[IRC_MESSAGE_LENGTH]; + struct message_flood *next; +}; + struct irc_bot_interface { int fd; bool isIn, isOn; @@ -46,6 +51,15 @@ struct irc_bot_interface { unsigned char fails; uint32 ip; unsigned short port; + /* messages flood protection */ + bool flood_protection_enabled; + int flood_protection_rate; + int flood_protection_burst; + int64 last_message_tick; + int messages_burst_count; + int queue_tid; + struct message_flood *message_current; + struct message_flood *message_last; /* */ struct channel_data *channel; /* */ @@ -67,7 +81,9 @@ struct irc_bot_interface { int (*identify_timer) (int tid, int64 tick, int id, intptr_t data); int (*join_timer) (int tid, int64 tick, int id, intptr_t data); /* */ - void (*send)(char *str); + int (*queue_timer) (int tid, int64 tick, int id, intptr_t data); + void (*queue) (char *str); + void (*send)(char *str, bool force); void (*relay) (const char *name, const char *msg); /* */ void (*pong) (int fd, char *cmd, char *source, char *target, char *msg); -- cgit v1.2.3-60-g2f50 From f0de696ce5cf22b4d9b3baac50a30c138a9b4ffd Mon Sep 17 00:00:00 2001 From: hemagx Date: Mon, 25 Apr 2016 00:55:08 +0200 Subject: Move irc-bot.c documentation to irc-bot.h interface a not interfaced function irc_privmsg_ctcp --- src/map/irc-bot.c | 107 ++++++++++---------------------------------------- src/map/irc-bot.h | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/map/irc-bot.c b/src/map/irc-bot.c index f1f76a616..5820ad2cf 100644 --- a/src/map/irc-bot.c +++ b/src/map/irc-bot.c @@ -48,10 +48,7 @@ struct irc_bot_interface *ircbot; char send_string[IRC_MESSAGE_LENGTH]; -/** - * Timer callback to (re-)connect to an IRC server - * @see timer->do_timer - */ +/// @copydoc irc_bot_interface::connect_timer() int irc_connect_timer(int tid, int64 tick, int id, intptr_t data) { struct hSockOpt opt; if( ircbot->isOn || ++ircbot->fails >= 3 ) @@ -71,10 +68,7 @@ int irc_connect_timer(int tid, int64 tick, int id, intptr_t data) { return 0; } -/** - * Timer callback to send identification commands to an IRC server - * @see timer->do_timer - */ +/// @copydoc irc_bot_interface::identify_timer() int irc_identify_timer(int tid, int64 tick, int id, intptr_t data) { if( !ircbot->isOn ) return 0; @@ -89,10 +83,7 @@ int irc_identify_timer(int tid, int64 tick, int id, intptr_t data) { return 0; } -/** - * Timer callback to join channels (and optionally send NickServ commands) - * @see timer->do_timer - */ +/// @copydoc irc_bot_interface::join_timer() int irc_join_timer(int tid, int64 tick, int id, intptr_t data) { if( !ircbot->isOn ) return 0; @@ -113,12 +104,7 @@ int irc_join_timer(int tid, int64 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 - */ +/// @copydoc irc_bot_interface::func_search() struct irc_func* irc_func_search(char* function_name) { int i; nullpo_retr(NULL, function_name); @@ -130,10 +116,7 @@ struct irc_func* irc_func_search(char* function_name) { return NULL; } -/** - * Parser for the IRC server connection - * @see do_sockets - */ +/// @copydoc irc_bot_interface::parse() int irc_parse(int fd) { char *parse_string = NULL, *p = NULL, *str_safe = NULL; @@ -167,16 +150,7 @@ 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) - */ +/// @copydoc irc_bot_interface::parse_source() void irc_parse_source(char *source, char *nick, char *ident, char *host) { int i, pos = 0; size_t len; @@ -200,12 +174,7 @@ void irc_parse_source(char *source, char *nick, char *ident, char *host) { } } -/** - * 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 - */ +/// @copydoc irc_bot_interface::parse_sub() void irc_parse_sub(int fd, char *str) { char source[180], command[60], buf1[IRC_MESSAGE_LENGTH], buf2[IRC_MESSAGE_LENGTH]; char *target = buf1, *message = buf2; @@ -235,11 +204,7 @@ void irc_parse_sub(int fd, char *str) { func->func(fd,command,source,target,message); } -/** - * Decides if an IRC Command should be queued or not, based on the flood protection settings. - * - * @param str Command to be checked - **/ +/// @copydoc irc_bot_interface::queue() void irc_queue(char *str) { struct message_flood *queue_entry = NULL; @@ -276,6 +241,7 @@ void irc_queue(char *str) } } +/// @copydoc irc_bot_interface::queue_timer() int irc_queue_timer(int tid, int64 tick, int id, intptr_t data) { struct message_flood *queue_entry = ircbot->message_current; @@ -296,10 +262,7 @@ int irc_queue_timer(int tid, int64 tick, int id, intptr_t data) return 0; } -/** - * Send a raw command to the irc server - * @param str Command to send - */ +/// @copydoc irc_bot_interface::send() void irc_send(char *str, bool force) { size_t len; @@ -319,20 +282,14 @@ void irc_send(char *str, bool force) WFIFOSET(ircbot->fd, len); } -/** - * Handler for the PING IRC command (send back a PONG) - * @see irc_parse_sub - */ +/// @copydoc irc_interface_bot::pong() void irc_pong(int fd, char *cmd, char *source, char *target, char *msg) { nullpo_retv(cmd); snprintf(send_string, IRC_MESSAGE_LENGTH, "PONG %s", cmd); ircbot->send(send_string, false); } -/** - * Handler for CTCP commands received via PRIVMSG - * @see irc_privmsg - */ +/// @copydoc irc_interface_bot::privmsg_ctcp() 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]; @@ -378,10 +335,7 @@ void irc_privmsg_ctcp(int fd, char *cmd, char *source, char *target, char *msg) } } -/** - * Handler for the PRIVMSG IRC command (action depends on the message contents) - * @see irc_parse_sub - */ +/// @copydoc irc_bot_interface::privmsg() void irc_privmsg(int fd, char *cmd, char *source, char *target, char *msg) { size_t len = msg ? strlen(msg) : 0; nullpo_retv(source); @@ -392,7 +346,7 @@ void irc_privmsg(int fd, char *cmd, char *source, char *target, char *msg) { 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); + ircbot->privmsg_ctcp(fd, command, source, target, message); #ifdef IRCBOT_DEBUG } else if (strcmpi(target, channel->config->irc_nick) == 0) { ShowDebug("irc_privmsg: Received message from %s: '%s'\n", source ? source : "(null)", msg); @@ -419,11 +373,7 @@ void irc_privmsg(int fd, char *cmd, char *source, char *target, char *msg) { } } -/** - * Handler for the JOIN IRC command (notify an in-game channel of users joining - * the IRC channel) - * @see irc_parse_sub - */ +/// @copydoc irc_bot_interface::userjoin() 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]; @@ -439,11 +389,7 @@ void irc_userjoin(int fd, char *cmd, char *source, char *target, char *msg) { } } -/** - * Handler for the PART and QUIT IRC commands (notify an in-game channel of - * users leaving the IRC channel) - * @see irc_parse_sub - */ +/// @copydoc irc_bot_interface::userleave() 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]; @@ -462,11 +408,7 @@ void irc_userleave(int fd, char *cmd, char *source, char *target, char *msg) { } } -/** - * 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 - */ +/// @copydoc irc_bot_interface::usernick() 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]; @@ -482,11 +424,7 @@ void irc_usernick(int fd, char *cmd, char *source, char *target, char *msg) { } } -/** - * Relay a chat message to the irc channel the bot is connected to - * @param name Sender's name - * @param msg Message text - */ +/// @copydoc irc_bot_interface::relay() void irc_relay(const char *name, const char *msg) { if (!ircbot->isIn) @@ -501,9 +439,7 @@ void irc_relay(const char *name, const char *msg) ircbot->send(send_string, false); } -/** - * IRC bot initializer - */ +/// @copydoc irc_bot_interface::init() void irc_bot_init(bool minimal) { /// Command handlers const struct irc_func irc_func_base[] = { @@ -554,9 +490,7 @@ void irc_bot_init(bool minimal) { timer->add(timer->gettick() + 7000, ircbot->connect_timer, 0, 0); } -/** - * IRC bot finalizer - */ +/// @copydoc irc_bot_interface::final() void irc_bot_final(void) { int i; @@ -619,6 +553,7 @@ void ircbot_defaults(void) { ircbot->pong = irc_pong; ircbot->privmsg = irc_privmsg; + ircbot->privmsg_ctcp = irc_privmsg_ctcp; ircbot->userjoin = irc_userjoin; ircbot->userleave = irc_userleave; diff --git a/src/map/irc-bot.h b/src/map/irc-bot.h index 949305bc9..f73426693 100644 --- a/src/map/irc-bot.h +++ b/src/map/irc-bot.h @@ -67,29 +67,132 @@ struct irc_bot_interface { struct irc_func **list; unsigned int size; } funcs; - /* */ + + /** + * IRC bot initializer + */ void (*init) (bool minimal); + + /** + * IRC bot finalizer + */ void (*final) (void); - /* */ + + /** + * Parser for the IRC server connection + * @see do_sockets + */ int (*parse) (int fd); + + /** + * 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 (*parse_sub) (int fd, char *str); + + /** + * 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 (*parse_source) (char *source, char *nick, char *ident, char *host); - /* */ + + /** + * 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* (*func_search) (char* function_name); - /* */ + + /** + * Timer callback to (re-)connect to an IRC server + * @see timer_interface::do_timer + */ int (*connect_timer) (int tid, int64 tick, int id, intptr_t data); + + /** + * Timer callback to send identification commands to an IRC server + * @see timer_interface::do_timer + */ int (*identify_timer) (int tid, int64 tick, int id, intptr_t data); + + /** + * Timer callback to join channels (and optionally send NickServ commands) + * @see timer_interface::do_timer + */ int (*join_timer) (int tid, int64 tick, int id, intptr_t data); - /* */ + + /** + * Timer callback to send queued IRC Commands + * @see timer_interface::do_timer + */ int (*queue_timer) (int tid, int64 tick, int id, intptr_t data); + + /** + * Decides if an IRC Command should be queued or not, based on the flood protection settings. + * + * @param str Command to be checked + */ void (*queue) (char *str); + + /** + * Send a raw command to the irc server + * @param str Command to send + */ void (*send)(char *str, bool force); + + /** + * Relay a chat message to the irc channel the bot is connected to + * @param name Sender's name + * @param msg Message text + */ void (*relay) (const char *name, const char *msg); - /* */ + + /** + * Handler for the PING IRC command (send back a PONG) + * @see irc_bot_interface::parse_sub + */ void (*pong) (int fd, char *cmd, char *source, char *target, char *msg); + + /** + * Handler for the PRIVMSG IRC command (action depends on the message contents) + * @see irc_bot_interface::parse_sub + */ void (*privmsg) (int fd, char *cmd, char *source, char *target, char *msg); + + /** + * Handler for CTCP commands received via PRIVMSG + * @see irc_bot_interface::privmsg + */ + void (*privmsg_ctcp) (int fd, char *cmd, char *source, char *target, char *msg); + + /** + * Handler for the JOIN IRC command (notify an in-game channel of users joining + * the IRC channel) + * @see irc_bot_interface::parse_sub + */ void (*userjoin) (int fd, char *cmd, char *source, char *target, char *msg); + + /** + * Handler for the PART and QUIT IRC commands (notify an in-game channel of + * users leaving the IRC channel) + * @see irc_bot_interface::parse_sub + */ void (*userleave) (int fd, char *cmd, char *source, char *target, char *msg); + + /** + * Handler for the NICK IRC commands (notify an in-game channel of users + * changing their name while in the IRC channel) + * @see irc_bot_interface::parse_sub + */ void (*usernick) (int fd, char *cmd, char *source, char *target, char *msg); }; -- cgit v1.2.3-60-g2f50