irctk

libircclient binding for scripts
git clone https://a3nm.net/git/irctk/
Log | Files | Refs | README

commit 1144bd75047abbcbda779e8b3e28a34904d50363
parent 0c62375e3f62420a2e488f2f7ce072ce6d88dd10
Author: Antoine Amarilli <ant.amarilli@free.fr>
Date:   Sun, 20 Feb 2011 03:25:26 +0100

done some cleanup, added non-functional -N, reconnect works

Diffstat:
irctk.c | 230+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 130 insertions(+), 100 deletions(-)

diff --git a/irctk.c b/irctk.c @@ -24,12 +24,13 @@ #define _GNU_SOURCE // TODO cli options for that -#define INITIAL_RETRY 15 +#define INITIAL_RETRY 5 #define FACTOR_RETRY 2 #define E_SESSION 1 #define E_CONNECT 2 #define E_THREAD 3 +#define E_BADNAME 4 #define DEFAULT_FIRST 0 #define DEFAULT_LAST_IN 1 @@ -43,9 +44,7 @@ // TODO get rid of that #define MAX_LEN 4096 -// TODO pseudo tracking - -// TODO default username is login pruned of bad chars +// TODO tracking of nicks //TODO const char *argp_program_version = "irctk 0.1"; //TODO const char *argp_program_bug_address = "<a3nm@a3nm.net>"; @@ -74,39 +73,23 @@ static struct argp_option options[] = { "Produce debug output on stderr", STANDARD, }, {"quiet", 'q', 0, 0, "Don't even display errors on stderr, fail silently", STANDARD, }, - /*{"no-command-to-event", 0, 0, 0, - "Don't generate events from messages with \"/quit\", \"/me\"...", - PARSING, }, // TODO - {"no-destination-prefix", 0, 0, 0, - "Don't look for []", - PARSING}, // TODO */ - {"event-to-nothing", 'n', 0, 0, - "Ignore server events (default).", - PARSING}, - {"event-to-command", 'c', 0, 0, - "Translate server events to messages with \"/quit\", \"/me\"...", - PARSING}, - {"event-to-message", 'm', 0, 0, - "Translate server events to a human-readable description", - PARSING}, - {"with-host", 'w', 0, 0, - "Keep the host info in pseudos" , - DISPLAY_SELECTION}, - /* TODO test, and make compatible with --own */ - {"own", 'o', 0, 0, - "Also display messages posted by self", - DISPLAY_SELECTION}, + {"interval", 'i', "secs", 0, "Wait delay between posted messages (default: 0 for tty stdin, 1 for file/pipe)", /* TODOponey ideally should not delay for bots, only for things piped, is there a * better way such as only waiting at the end? */ MISC}, - {"username", 'U', "name", 0, - "Unix username (default: irctk)", - NAME}, - {"realname", 'R', "name", 0, - "Full name (default: irctk)", - NAME}, + /*{"no-auto-join", 0, 0, 0, + "Refuse to send any message to channels not specified on the command line (default is to join as needed)", MISC }, // TODO*/ + + {"synchronous", 's', 0, 0, + "Will wait for a multiline, blankline-terminated answer on stdin to all lines passed on stdout, and will bufferize activity before that", COM_MODE }, // TODO + /*{"exec", 'e', 0, 0, + "Pipe each message to this command", COM_MODE }, // TODO and support several of them + {"exec-argv", 'E', 0, 0, + "Run this command on each message, passing the channel, nick and message as $1, $2, and $3", COM_MODE }, + */ + {"default-always-first", '0', 0, 0, "Post messages to the first specified channel by default", CHANNEL_SELECTION}, @@ -122,13 +105,7 @@ static struct argp_option options[] = { {"show-inferred", 'P', 0, 0, "When inferring a next chan with --default-last-posted, display it on stderr", CHANNEL_SELECTION}, - {"synchronous", 's', 0, 0, - "Will wait for a multiline, blankline-terminated answer on stdin to all lines passed on stdout, and will bufferize activity before that", COM_MODE }, - /*{"exec", 'e', 0, 0, - "Pipe each message to this command", COM_MODE }, // TODO and support several of them - {"exec-argv", 'E', 0, 0, - "Run this command on each message, passing the channel, nick and message as $1, $2, and $3", COM_MODE }, - */ + {"reply", 'r', 0, 0, "When inferring a destination channel automatically with --last-active, also infer a destination nick, and prefix lines with this nick (blank line indicates the end of an answer)", BOT_OPTIONS }, /* TODO externalize those two features as a wrapper script */ @@ -136,8 +113,41 @@ static struct argp_option options[] = { "Only keep messages directly addressed to us (in private channel or with lines prefixed by our nick)", BOT_OPTIONS }, {"filter-prune", 'F', 0, 0, "Only give the message body without the channel, nick or address (useful with -fr)", BOT_OPTIONS }, - /*{"no-auto-join", 0, 0, 0, - "Refuse to send any message to channels not specified on the command line (default is to join as needed)", MISC }, // TODO*/ + + {"with-host", 'w', 0, 0, + "Keep the host info in pseudos" , + DISPLAY_SELECTION}, + /* TODO test, and make compatible with --own */ + {"own", 'o', 0, 0, + "Also display messages posted by self", + DISPLAY_SELECTION}, + + /*{"no-command-to-event", 0, 0, 0, + "Don't generate events from messages with \"/quit\", \"/me\"...", + PARSING, }, // TODO + {"no-destination-prefix", 0, 0, 0, + "Don't look for []", + PARSING}, // TODO */ + {"event-to-nothing", 'n', 0, 0, + "Ignore server events (default).", + PARSING}, + {"event-to-command", 'c', 0, 0, + "Translate server events to messages with \"/quit\", \"/me\"...", + PARSING}, + {"event-to-message", 'm', 0, 0, + "Translate server events to a human-readable description", + PARSING}, + + {"username", 'U', "name", 0, + "Unix username (default: irctk)", + NAME}, + {"realname", 'R', "name", 0, + "Full name (default: irctk)", + NAME}, + {"force-nick", 'N', 0, 0, //TODO + "Don't give up if the nick is invalid (revert to default) or already taken (add trailing underscores)", + NAME}, + { 0 } }; @@ -147,29 +157,38 @@ static struct argp_option options[] = { struct arguments { char *nick; - char *username; - char *realname; char *server; int port; char **channels; int n_channels; - int interval; + int verbosity; - int command; + + int interval; + + int default_destination; + int show_inferred; + + int with_host; int own; + + int event_to; + + char *username; + int username_set; // to get nick as default username + char *realname; + int force_nick; + + int command; char * raw_dest; char ready; char last_chan_in[MAX_LEN]; char last_nick_in[MAX_LEN]; char last_chans_out[MAX_LEN]; - int default_destination; - int show_prefix; int show_nick_prefix; - int with_host; int filter; int prune; int command_to_event; - int event_to; }; typedef struct arguments irc_ctx_t; @@ -184,21 +203,30 @@ void initialize_args() args.port = 6667; args.channels = (char**) NULL; args.n_channels = 0; - args.interval = isatty(fileno(stdin))?0:1000000; + args.verbosity = 0; + + args.interval = isatty(fileno(stdin))?0:1000000; + + args.default_destination = DEFAULT_LAST_IN; + args.show_inferred = 0; + args.with_host = 0; + args.own = 0; + + args.event_to = NOTHING; + + args.username_set = 0; + args.username = args.nick; + args.realname = "irctk"; + args.force_nick = 0; + args.filter = 0; args.prune = 0; - args.command_to_event = 1; - args.own = 0; args.last_chan_in[0] = 0; args.last_nick_in[0] = 0; args.last_chans_out[0] = 0; - args.default_destination = DEFAULT_LAST_IN; - args.show_prefix = 0; - args.event_to = NOTHING; - args.username = "irctk"; - args.realname = "irctk"; + args.command_to_event = 1; } @@ -232,6 +260,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) arguments->interval = (int) (atof(arg) * 1000000.); break; case 'U': + arguments->username_set = 1; arguments->username = arg; break; case 'R': @@ -266,7 +295,10 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) arguments->default_destination = DEFAULT_ALL; break; case 'P': - arguments->show_prefix = 1; + arguments->show_inferred = 1; + break; + case 'N': + arguments->force_nick = 1; break; case ARGP_KEY_NO_ARGS: @@ -278,9 +310,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) } else { switch(key) { - /* TODO shouldn't allow options when a channel was seen */ case ARGP_KEY_ARG: - //debug("saw an arg"); if (state->arg_num == 0) { i = -1; @@ -292,6 +322,8 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) { arguments->raw_dest[i] = 0; arguments->nick = arguments->raw_dest; + if (!arguments->username_set) + arguments->username = arguments->raw_dest; arguments->server = arguments->raw_dest + i + 1; } if (arguments->raw_dest[i] == ':') @@ -508,17 +540,16 @@ int cmd_msg(irc_session_t *s, char* target, const char* line) THREAD_FUNCTION(irc_listen) { - irc_session_t * sp = (irc_session_t *) arg; - - irc_run(sp); - - return 0; + irc_session_t * sp = (irc_session_t *) arg; + irc_run(sp); + return 0; } void dump_event (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) { char buf[512]; // TODO danger int cnt; + int rsl; buf[0] = '\0'; @@ -530,6 +561,21 @@ void dump_event (irc_session_t * session, const char * event, const char * origi strcat (buf, params[cnt]); } + if (!strcmp(event, "432")) + { + /* bad nick */ + if (args.force_nick) + { + // TODO does not work + info("Invalid nick, reverting to \"irctk\""); + args.nick = "irctk"; + rsl = irc_cmd_nick (session, "irctk"); + } else { + // TODO abort + die(E_BADNAME, "Invalid nick!"); + } + } + debug("Event \"%s\", origin: \"%s\", params: %d [%s]", event, origin ? origin : "NULL", cnt, buf); } @@ -842,7 +888,7 @@ irc_session_t* do_connect() irc_set_ctx (s, &args); debug_args(); - debug("Trying to connect to %s port %d with nick %s...", + debug("Connecting to %s port %d with nick %s...", args.server, args.port, args.nick); @@ -852,7 +898,7 @@ irc_session_t* do_connect() return s; } -int start () +int start (int max_wait) { irc_session_t * s; thread_id_t tid; @@ -862,13 +908,12 @@ int start () long tp; int first = 1; int waiting = 0; - int max_wait = INITIAL_RETRY; s = do_connect(); debug("Connection request successful!"); - debug("Trying to create thread..."); + debug("Starting thread..."); if (CREATE_THREAD (&tid, irc_listen, s)) die(E_THREAD, "CREATE_THREAD failed: %s", strerror(errno)); // TODO spurious \n @@ -877,57 +922,42 @@ int start () line = (char*) malloc(size+1); - if (args.show_prefix && args.default_destination == DEFAULT_LAST_OUT) + if (args.show_inferred && args.default_destination == DEFAULT_LAST_OUT) fprintf(stderr, "[%s] ", args.last_chans_out); gettimeofday(&tp1, NULL); /* this is here early in order to get the connection working asap * TODO refactor it with the other copy! */ - /* TODO THIS DOESN't WORK and INTRODUCES DELAYS! */ - while (!args.ready) + if (!args.ready) { waiting = 0; debug("Waiting for the connection to be ready..."); while (waiting < max_wait && !args.ready) { - debug("waiting"); + debug("waiting (%d)", waiting); // TODO manage timeouts, try reconnects, handle errors, etc. // TODO manage duplicate pseudos sleep(1); waiting++; } - if (args.ready) break; - max_wait *= FACTOR_RETRY; - info("Trying to reconnect for %d seconds...", max_wait); - irc_disconnect(s); - args.ready = 0; - sleep(1); - s = do_connect(); + if (!args.ready) { + debug("nick is %s\n", args.nick); + info("Connection timed out after %d seconds, retrying...", max_wait); + irc_disconnect(s); + args.ready = 0; + sleep(1); + return start(max_wait * FACTOR_RETRY); + } } while ( (res = getline((char**) &line, (size_t*) &size, stdin)) != -1 ) { debug("startloop : got %s, waiting", line); - while (!args.ready) + if (!args.ready) { - waiting = 0; - debug("Waiting for connection"); - while (waiting < max_wait) - { - debug("Waiting"); - // TODO manage timeouts, try reconnects, handle errors, etc. - // TODO manage duplicate pseudos - sleep(1); - waiting++; - } - if (args.ready) break; - max_wait *= FACTOR_RETRY; - info("Trying to reconnect for %d seconds...", max_wait); - irc_disconnect(s); - args.ready = 0; - sleep(1); - s = do_connect(); + info("Connection lost, reconnecting..."); + return start(max_wait); } debug("ready", line); @@ -960,7 +990,7 @@ int start () cmd_msg(s, "", line); } - if (args.show_prefix && args.default_destination == DEFAULT_LAST_OUT) + if (args.show_inferred && args.default_destination == DEFAULT_LAST_OUT) fprintf(stderr, "[%s] ", args.last_chans_out); usleep(args.interval); @@ -989,7 +1019,7 @@ int main (int argc, char **argv) //debug_args(); - return start(); + return start(INITIAL_RETRY); }