irctk

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

commit 30cee322c1a4f18825a2ca3de3e0829a73a7b499
parent e180c07caf16b063524b9a8d5c42572e84810537
Author: Antoine Amarilli <ant.amarilli@free.fr>
Date:   Fri, 21 Jan 2011 06:04:19 +0100

cleanup, experimental additions

Diffstat:
irctk.c | 435++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 233 insertions(+), 202 deletions(-)

diff --git a/irctk.c b/irctk.c @@ -35,7 +35,7 @@ #include <unistd.h> #include <sys/time.h> -#include "libircclient.h" +#include <libircclient/libircclient.h> #include <unistd.h> #include <pthread.h> @@ -55,9 +55,14 @@ #define DEFAULT_FIRST 0 #define DEFAULT_LAST_IN 1 #define DEFAULT_LAST_OUT 2 +#define DEFAULT_ALL 3 + // TODO #define MAX_LEN 4096 + + + //const char *argp_program_version = "irctk 0.1"; //const char *argp_program_bug_address = "<a3nm@a3nm.net>"; const char *argp_program_version = NULL; @@ -79,33 +84,33 @@ static char args_doc[] = "[NICK@]SERVER[:PORT] [CHANNEL]..."; #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", STANDARD, }, {"quiet", 'q', 0, 0, "Don't even display errors on stderr, fail silently", STANDARD, }, - {"no-command-to-event", 0, 0, 0, + /*{"no-command-to-event", 0, 0, 0, "Don't generate events from messages with \"/quit\", \"/me\"...", - PARSING, }, /* TODO */ + PARSING, }, // TODO {"no-destination-prefix", 0, 0, 0, "Don't look for []", - PARSING}, /* TODO */ + PARSING}, // TODO {"event-to-command", 'c', 0, 0, "Translate server events to messages with \"/quit\", \"/me\"...", - PARSING}, /* TODO */ + PARSING}, // TODO {"event-to-message", 'm', 0, 0, "Translate server events to a human-readable description", - PARSING}, /* TODO */ + PARSING}, // TODO */ {"with-host", 'w', 0, 0, - "Keep the host info in pseudos" , /* TODO use irc_stripnick */ + "Keep the host info in pseudos" , DISPLAY_SELECTION}, - /* TODO, and make compatible with --own */ + /* TODO 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)", - /* TODO should not delay for bots, only for things piped, is there a + /* TODOponey ideally 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", '0', 0, 0, @@ -117,25 +122,28 @@ static struct argp_option options[] = { {"default-last-posted", 'p', 0, 0, "Post messages to the last posted channel by default", CHANNEL_SELECTION}, + {"default-all", 'l', 0, 0, + "Post messages to all specified channels by default", + CHANNEL_SELECTION}, {"show-inferred", 'P', 0, 0, "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, 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", '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 }, - /* TODO and allow to pipe on argv rather than stdin */ + */ {"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 terminates)", BOT_OPTIONS }, + /* TODO externalize those two features as a wrapper script */ {"filter", 'f', 0, 0, "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 }, + /*{"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*/ { 0 } }; @@ -144,27 +152,27 @@ static struct argp_option options[] = { */ struct arguments { - char ** channels; - int n_channels; - int interval; - int verbosity; - int quiet; - int command; - char * nick; - char * server; - int port; - int own; - 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; + char *nick; + char *server; + int port; + char **channels; + int n_channels; + int interval; + int verbosity; + int command; + int own; + 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; }; typedef struct arguments irc_ctx_t; @@ -172,7 +180,137 @@ typedef struct arguments irc_ctx_t; /* The arguments */ struct arguments args; +void initialize_args() +{ + args.nick = "irctk"; + args.server = NULL; + args.port = 6667; + args.channels = (char**) NULL; + args.n_channels = 0; + args.interval = isatty(fileno(stdin))?0:1000000; + args.verbosity = 0; + args.with_host = 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; +} + + + + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + struct arguments *arguments = state->input; + int i; + + if (key != ARGP_KEY_ARG && key != ARGP_KEY_END && (! arguments->channels)) + { + switch (key) + { + case 'v': + arguments->verbosity = 1; + break; + case 'q': + arguments->verbosity = -1; + break; + case 'i': + arguments->interval = (int) (atof(arg) * 1000000.); + break; + case 'o': + arguments->own = 1; + break; + case 'w': + arguments->with_host = 1; + break; + case 'r': + arguments->show_nick_prefix = 1; + break; + case '0': + arguments->default_destination = DEFAULT_FIRST; + break; + case 'f': + arguments->filter = 1; + break; + case 'F': + arguments->filter = 1; + arguments->prune = 1; + break; + case 'a': + arguments->default_destination = DEFAULT_LAST_IN; + break; + case 'p': + arguments->default_destination = DEFAULT_LAST_OUT; + break; + case 'l': + arguments->default_destination = DEFAULT_ALL; + break; + case 'P': + arguments->show_prefix = 1; + break; + + case ARGP_KEY_NO_ARGS: + argp_usage (state); + + default: + return ARGP_ERR_UNKNOWN; + } + } 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; + arguments->raw_dest = arg; + arguments->server = arg; + while (arguments->raw_dest[++i]) + { + if (arguments->raw_dest[i] == '@') + { + arguments->raw_dest[i] = 0; + arguments->nick = arguments->raw_dest; + arguments->server = arguments->raw_dest + i + 1; + } + if (arguments->raw_dest[i] == ':') + { + arguments->raw_dest[i] = 0; + arguments->port = atoi(arguments->raw_dest + i + 1); + } + } + } + else { + if (arguments->channels == NULL) + { + //debug("start with %s, at %d", arg, state->arg_num); + arguments->channels = state->argv + (state->next - 1); + } + arguments->n_channels++; + } + break; + + case ARGP_KEY_END: + if (state->arg_num < 1) + /* TODO rather test if server is still null */ + argp_usage (state); + break; + } + + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, doc }; + +/* TODO2 refactor those 3 */ void info(const char *err, ...) { va_list ap; @@ -191,55 +329,33 @@ void debug(const char *err, ...) 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; - args.with_host = 0; - args.filter = 0; - args.prune = 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.interval = isatty(fileno(stdin))?0:1000000; - args.own = 0; - args.nick = "irctk"; - args.port = 6667; - 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; - debug("coucou"); - debug_args(); // TODO this is debug -} void die(int val, const char *err, ...) { - /* never die, always retry with info messages */ + /* TODO avoid dying, rather retry with info messages */ va_list ap; va_start(ap, err); - fprintf(stderr, "Error: "); + fprintf(stderr, "error: "); vfprintf(stderr, err, ap); fprintf(stderr, "\n"); exit(val); } +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); +} + + + + + char* first_chan() { @@ -250,16 +366,49 @@ char* first_chan() int do_cmd_msg(irc_session_t *s, const char* chan, const char* line) { - int rsl; - /* TODO ask the server to notify, if possible */ - /* TODO find a way to get real host name (part after !) */ + int rsl = -1; + + /* TODOponey ask the server to notify, if possible */ + /* TODO2 find a way to get real host name (part after !) */ - rsl = irc_cmd_msg (s, chan, line); + /* TODO2 provide a means to escape '/' */ + if (line[0] == '/' && args.command_to_event) { + if ( strstr (line[1], "nick ") == line[1] ) + { + rsl = irc_cmd_nick (s, line+6); + } else if (strstr (line[1], "part") == line[1]) { + if (!line[5] || line[5] == '\n') + { + rsl = irc_cmd_part(s, chan); + } else if (line[5] == ' ') { + rsl = irc_cmd_part(s, line + 6); + } + } else if (strstr (line[1], "join") == line[1]) { + if (!line[5] || line[5] == '\n') + { + rsl = irc_cmd_join(s, chan, NULL); + } else if (line[5] == ' ') { + rsl = irc_cmd_join(s, line + 6, NULL); + } + } else if (strstr (line[1], "quit") == line[1]) { + /* TODO notify main thread that it is over */ + if (!line[5] || line[5] == '\n') + { + rsl = irc_cmd_quit(s, NULL); + } else if (line[5] == ' ') { + rsl = irc_cmd_quit(s, line + 6); + } + } + } else { + rsl = irc_cmd_msg (s, chan, line); + } /* TODO manage errors */ if (rsl) return rsl; + if (args.own) { + /* TODO join, quit, etc. take place in a special '[]' channel */ printf("[%s] <%s> %s", chan, args.nick, line); fflush(stdout); } @@ -306,6 +455,11 @@ int cmd_msg(irc_session_t *s, char* target, const char* line) case DEFAULT_LAST_OUT: target = args.last_chans_out; break; + + case DEFAULT_ALL: + /* TODO */ + target = args.last_chans_out; + break; } } @@ -330,119 +484,6 @@ int cmd_msg(irc_session_t *s, char* target, const char* line) return rsl; } - - -/* Parse a single option. */ - static error_t -parse_opt (int key, char *arg, struct argp_state *state) -{ - /* Get the input argument from argp_parse, which we - know is a pointer to our arguments structure. */ - struct arguments *arguments = state->input; - int i; - - if (key != ARGP_KEY_ARG && key != ARGP_KEY_END && (! arguments->channels)) - { - switch (key) - { - case 'v': - arguments->verbosity = 1; - break; - case 'q': - arguments->verbosity = -1; - break; - case 'i': - // TODO use float, and be more robust - 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; - break; - case 'w': - arguments->with_host = 1; - break; - case 'r': - arguments->show_nick_prefix = 1; - break; - case '0': - arguments->default_destination = DEFAULT_FIRST; - break; - case 'f': - arguments->filter = 1; - break; - case 'F': - arguments->filter = 1; - arguments->prune = 1; - break; - case 'a': - arguments->default_destination = DEFAULT_LAST_IN; - break; - case 'p': - arguments->default_destination = DEFAULT_LAST_OUT; - break; - case 'P': - arguments->show_prefix = 1; - break; - - case ARGP_KEY_NO_ARGS: - argp_usage (state); - - default: - return ARGP_ERR_UNKNOWN; - } - } else { - switch(key) - { - /* TODO option exclusion doesn't work! */ - case ARGP_KEY_ARG: - debug("saw an arg"); - if (state->arg_num == 0) - { - i = -1; - arguments->raw_dest = arg; - arguments->server = arg; - while (arguments->raw_dest[++i]) - { - if (arguments->raw_dest[i] == '@') - { - arguments->raw_dest[i] = 0; - arguments->nick = arguments->raw_dest; - arguments->server = arguments->raw_dest + i + 1; - } - if (arguments->raw_dest[i] == ':') - { - arguments->raw_dest[i] = 0; - arguments->port = atoi(arguments->raw_dest + i + 1); - } - } - } - else { - if (arguments->channels == NULL) - { - debug("start with %s, at %d", arg, state->arg_num); - arguments->channels = state->argv + (state->next - 1); - } - arguments->n_channels++; - } - break; - - case ARGP_KEY_END: - if (state->arg_num < 1) - /* Not enough arguments. */ - argp_usage (state); - break; - } - - } - return 0; -} - -/* Our argp parser. */ -static struct argp argp = { options, parse_opt, args_doc, doc }; - - - THREAD_FUNCTION(irc_listen) { irc_session_t * sp = (irc_session_t *) arg; @@ -630,14 +671,6 @@ void event_channel (irc_session_t * session, const char * event, const char * or /*irc_target_get_nick (origin, nickbuf, sizeof(nickbuf)); - if ( !strcmp (params[1], "quit") ) - irc_cmd_quit (session, "of course, Master!"); - - if ( !strcmp (params[1], "help") ) - { - irc_cmd_msg (session, params[0], "quit, help, dcc chat, dcc send, ctcp"); - } - if ( !strcmp (params[1], "ctcp") ) { irc_cmd_ctcp_request (session, nickbuf, "PING 223"); @@ -668,9 +701,6 @@ void event_channel (irc_session_t * session, const char * event, const char * or if ( strstr (params[1], "mode ") == params[1] ) irc_cmd_channel_mode (session, params[0], params[1] + 5); - if ( strstr (params[1], "nick ") == params[1] ) - irc_cmd_nick (session, params[1] + 5); - if ( strstr (params[1], "whois ") == params[1] ) irc_cmd_whois (session, params[1] + 5);*/ } @@ -874,16 +904,17 @@ int start () int main (int argc, char **argv) { - /* Default values. */ initialize_args(); - debug_args(); + //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_args(); + //debug_args(); return start(); } + +