irctk

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

commit 8bfc9b7f08c5fc3e00fdcec7c3ce034250a98adc
parent a8f487c9aaacb51f252ab46641ce0ddcc0ae96a2
Author: Antoine Amarilli <ant.amarilli@free.fr>
Date:   Sun, 16 Jan 2011 13:44:22 +0100

more help, ordered help, more options, buggy auto-reconnect

Diffstat:
Makefile | 8++++----
irctk.c | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
2 files changed, 180 insertions(+), 63 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ # $Id: Makefile.in 42 2004-10-10 16:16:15Z gyunaev $ CC = gcc CXX = g++ -CFLAGS = -Wall -O3 -DENABLE_THREADS -D_REENTRANT -LIBS = ../src/libircclient.a -lpthread -lnsl +CFLAGS = -Wall -g -DENABLE_THREADS -D_REENTRANT +LIBS = /usr/lib/libircclient.a -lpthread -lnsl INCLUDES=-I../include EXAMPLES=irctk @@ -21,8 +21,8 @@ distclean: clean .c.o: @echo "Compiling $<" - @$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< + $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< .cpp.o: @echo "Compiling $<" - @$(CXX) $(CFLAGS) $(INCLUDES) -c -o $@ $< + $(CXX) $(CFLAGS) $(INCLUDES) -c -o $@ $< diff --git a/irctk.c b/irctk.c @@ -32,6 +32,8 @@ #include <stdlib.h> #include <errno.h> #include <argp.h> +#include <unistd.h> +#include <sys/time.h> #include "libircclient.h" @@ -43,6 +45,9 @@ #define thread_id_t pthread_t #define _GNU_SOURCE +#define INITIAL_RETRY 3 +#define FACTOR_RETRY 2 + #define E_SESSION 1 #define E_CONNECT 2 #define E_THREAD 3 @@ -53,8 +58,10 @@ // TODO #define MAX_LEN 1024 -const char *argp_program_version = "irctk 0.1"; -const char *argp_program_bug_address = "<a3nm@a3nm.net>"; +//const char *argp_program_version = "irctk 0.1"; +//const char *argp_program_bug_address = "<a3nm@a3nm.net>"; +const char *argp_program_version = NULL; +const char *argp_program_bug_address = NULL; /* Program documentation. */ static char doc[] = @@ -63,51 +70,70 @@ static char doc[] = /* A description of the arguments we accept. */ static char args_doc[] = "[NICK@]SERVER[:PORT] [CHANNEL]..."; +#define STANDARD 0 +#define MISC 1 +#define COM_MODE 2 +#define CHANNEL_SELECTION 3 +#define BOT_OPTIONS 4 +#define DISPLAY_SELECTION 5 +#define PARSING 6 /* The options we understand. */ static struct argp_option options[] = { /* TODO Display by theme not by alpha order */ {"verbose", 'v', 0, 0, - "Produce debug output on stderr" }, + "Produce debug output on stderr", STANDARD, }, {"quiet", 'q', 0, 0, - "Don't even display errors on stderr, fail silently" }, + "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\"..." }, /* TODO */ + "Don't generate events from messages with \"/quit\", \"/me\"...", + PARSING, }, /* TODO */ {"no-destination-prefix", 0, 0, 0, - "Don't look for []" }, /* TODO */ + "Don't look for []", + PARSING}, /* TODO */ {"event-to-command", 'c', 0, 0, - "Translate server events to messages with \"/quit\", \"/me\"..." }, /* TODO */ + "Translate server events to messages with \"/quit\", \"/me\"...", + PARSING}, /* TODO */ {"event-to-message", 'm', 0, 0, - "Translate server events to a human-readable description" }, /* TODO */ + "Translate server events to a human-readable description", + PARSING}, /* TODO */ {"with-host", 'w', 0, 0, - "Keep the host info in pseudos" }, + "Keep the host info in pseudos" , /* TODO use irc_stripnick */ + DISPLAY_SELECTION}, /* TODO, and make compatible with --own */ {"own", 'o', 0, 0, - "Also display messages posted by self" }, + "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)" }, -/* TODO detect if stdin is a tty for delay configuration */ + "Wait delay between posted messages (default: 0 for tty stdin, 1 for file/pipe)", + /* TODO should not delay for bots, only for things piped, is there a + * better way such as only waiting at the end? */ + MISC}, {"default-always-first", 'f', 0, 0, - "Post messages to the first specified channel by default" }, + "Post messages to the first specified channel by default", + CHANNEL_SELECTION}, {"default-last-active", 'a', 0, 0, - "Post messages to the last active channel by default" }, + "Post messages to the last active channel by default (default)", + CHANNEL_SELECTION}, {"default-last-posted", 'p', 0, 0, - "Post messages to the last posted channel by default" }, + "Post messages to the last posted channel by default", + CHANNEL_SELECTION}, {"show-inferred", 'P', 0, 0, - "When inferring a next chan with --default-last-posted, display it on stderr" }, + "When inferring a next chan with --default-last-posted, display it on stderr", + CHANNEL_SELECTION}, /* TODO for this option, it would be cool to display the channel name on stderr, for the poor man's client*/ {"synchronous", 's', 0, 0, - "Will wait for a multiline, blank line-terminated answer on stdin to all lines passed on stdout, and will bufferize activity before that" }, + "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" }, /* TODO and support several of them */ + "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" }, + "Run this command on each message, passing the channel, nick and message as $1, $2, and $3", COM_MODE }, /* TODO and allow to pipe on argv rather than stdin */ {"reply", 'r', 0, 0, - "When inferring a destination channel automatically, also infer a destination nick, and prefix lines with this nick" }, + "When inferring a destination channel automatically, also infer a destination nick, and prefix lines with this nick", BOT_OPTIONS }, {"filter", 'f', 0, 0, - "Only keep messages directly addressed to us (in private channel or with lines prefixed by our nick)" }, + "Only keep messages directly addressed to us (in private channel or with lines prefixed by our nick)", 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)" }, + "Refuse to send any message to channels not specified on the command line (default is to join as needed)", MISC }, { 0 } }; @@ -119,7 +145,8 @@ struct arguments char ** channels; int n_channels; int interval; - int verbose; + int verbosity; + int quiet; int command; char * nick; char * server; @@ -138,12 +165,48 @@ typedef struct arguments irc_ctx_t; /* The arguments */ struct arguments args; + +void info(const char *err, ...) +{ + va_list ap; + va_start(ap, err); + if (args.verbosity >= 0) + { + fprintf(stderr, "info: "); + vfprintf(stderr, err, ap); + fprintf(stderr, "\n"); + } +} + +void debug(const char *err, ...) +{ + va_list ap; + va_start(ap, err); + if (args.verbosity > 0) + { + /* TODO refactor with info */ + fprintf(stderr, "debug: "); + vfprintf(stderr, err, ap); + fprintf(stderr, "\n"); + } +} +void debug_args() +{ + debug("n_channels = %d\ninterval = %d\nnick = %s\nserver = %s\nport = %d\n", + args.n_channels, args.interval, args.nick, args.server, args.port); +} + void initialize_args() { + + args.verbosity = 0; + debug("coucou1"); + debug_args(); // TODO this is debug args.n_channels = 0; + debug("coucou2"); + debug_args(); // TODO this is debug args.channels = (char**) NULL; - args.verbose = 0; - args.interval = isatty(fileno(stdin))?0:1; + args.interval = isatty(fileno(stdin))?0:1000000; args.own = 0; args.nick = "irctk"; args.port = 6667; @@ -151,10 +214,13 @@ void initialize_args() args.last_chans_out[0] = 0; args.default_destination = DEFAULT_LAST_IN; args.show_prefix = 0; + debug("coucou"); + debug_args(); // TODO this is debug } void die(int val, const char *err, ...) { + /* never die, always retry with info messages */ va_list ap; va_start(ap, err); fprintf(stderr, "Error: "); @@ -164,18 +230,6 @@ void die(int val, const char *err, ...) } -void debug(const char *err, ...) -{ - va_list ap; - va_start(ap, err); - if (args.verbose) - { - fprintf(stderr, "debug: "); - vfprintf(stderr, err, ap); - fprintf(stderr, "\n"); - } -} - char* first_chan() { if (args.n_channels) @@ -266,12 +320,15 @@ parse_opt (int key, char *arg, struct argp_state *state) switch (key) { case 'v': - arguments->verbose = 1; + arguments->verbosity = 1; + break; + case 'q': + arguments->verbosity = -1; break; case 'i': // TODO use float, and be more robust - debug ("seen i, nchan is %d", arguments->n_channels); - arguments->interval = atoi(arg); + arguments->interval = (int) (atof(arg) * 1000000.); + debug ("seen i, nchan is %d, took value %d", arguments->n_channels, arguments->interval); break; case 'o': arguments->own = 1; @@ -371,7 +428,6 @@ void dump_event (irc_session_t * session, const char * event, const char * origi strcat (buf, params[cnt]); } - debug("Event \"%s\", origin: \"%s\", params: %d [%s]", event, origin ? origin : "NULL", cnt, buf); } @@ -382,6 +438,9 @@ void event_join (irc_session_t * session, const char * event, const char * origi irc_cmd_user_mode (session, "+i"); debug("Joined!"); + + /* Check for that before talking */ + /*irc_ctx_t * ctx = (irc_ctx_t *) irc_get_ctx (session); if ( !origin ) @@ -569,15 +628,10 @@ void event_numeric (irc_session_t * session, unsigned int event, const char * or dump_event (session, buf, origin, params, count); } - -int start () +irc_session_t* do_connect() { + irc_session_t *s; irc_callbacks_t callbacks; - irc_session_t * s; - thread_id_t tid; - char *line = NULL; - int res, size=100; - memset (&callbacks, 0, sizeof(callbacks)); callbacks.event_connect = event_connect; @@ -608,12 +662,31 @@ int start () irc_set_ctx (s, &args); + debug_args(); debug("Trying to connect to %s port %d with nick %s...", args.server, args.port, args.nick); + if (irc_connect (s, args.server, args.port, 0, args.nick, 0, 0)) die(E_CONNECT, "Could not connect: %s\n", irc_strerror (irc_errno(s))); + return s; +} + +int start () +{ + irc_session_t * s; + thread_id_t tid; + char *line = NULL; + int res, size=100; + struct timeval tp1, tp2; + 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..."); @@ -628,17 +701,63 @@ int start () if (args.show_prefix && args.default_destination == DEFAULT_LAST_OUT) fprintf(stderr, "[%s] ", args.last_chans_out); - while ( (res = getline((char**) &line, (size_t*) &size, stdin)) != -1 ) + 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) { - while (!args.ready) + waiting = 0; + debug("Waiting for the connection to be ready..."); + while (waiting < max_wait) { - debug("Waiting for the connection to be ready..."); + debug("waiting"); // TODO manage timeouts, try reconnects, handle errors, etc. // TODO manage duplicate pseudos sleep(1); + waiting++; } - debug("got %s", line); + 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(); + } + while ( (res = getline((char**) &line, (size_t*) &size, stdin)) != -1 ) + { + debug("got %s, waiting", line); + while (!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(); + } + + debug("ready", line); + + gettimeofday(&tp2, NULL); + tp = 1000000*(tp2.tv_sec - tp1.tv_sec) + tp2.tv_usec - tp1.tv_usec; + if (tp < args.interval && !first) + usleep(args.interval - tp); + first = 0; if (line[0] == '[') { @@ -659,11 +778,11 @@ int start () if (args.show_prefix && args.default_destination == DEFAULT_LAST_OUT) fprintf(stderr, "[%s] ", args.last_chans_out); - sleep(args.interval); + usleep(args.interval); } line = NULL; - sleep(args.interval); + usleep(2*args.interval); irc_disconnect(s); free(line); @@ -675,15 +794,13 @@ int main (int argc, char **argv) /* Default values. */ initialize_args(); - debug ("isatty? %s\n", isatty(fileno(stdin))?"yes":"no"); - + debug_args(); argp_parse (&argp, argc, argv, 0, 0, &args); strcpy(args.last_chan_in, first_chan()); strcpy(args.last_chans_out, first_chan()); - - debug("n_channels = %d\ninterval = %d\nnick = %s\nserver = %s\nport = %d\n", - args.n_channels, args.interval, args.nick, args.server, args.port); + + debug_args(); return start(); }