diff --git a/NEWS b/NEWS index 9b3772b..97de82a 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ * degesch: added detection of pasting, so that it doesn't trigger other keyboard shortcuts, such as for autocomplete + * degesch: added auto-away capability + * degesch: added an /oper command * degesch: libedit backend works again diff --git a/README.adoc b/README.adoc index 03f72c1..98e13f7 100644 --- a/README.adoc +++ b/README.adoc @@ -24,7 +24,7 @@ This is the largest application within the project. It has most of the stuff you'd expect of an IRC client, such as being able to set up multiple servers, a powerful configuration system, integrated help, text formatting, CTCP queries, automatic splitting of overlong messages, autocomplete, logging to file, -command aliases and rudimentary support for Lua scripting. +auto-away, command aliases and rudimentary support for Lua scripting. kike ---- diff --git a/degesch.c b/degesch.c index f4deab3..dc16c93 100644 --- a/degesch.c +++ b/degesch.c @@ -1207,6 +1207,7 @@ struct server int nick_counter; ///< Iterates "nicks" when registering struct str irc_user_mode; ///< Our current user modes char *irc_user_host; ///< Our current user@host + bool autoaway_active; ///< Autoaway is currently active bool cap_echo_message; ///< Whether the server echos messages @@ -1480,6 +1481,7 @@ struct app_context struct poller_timer flush_timer; ///< Flush all open files (e.g. logs) struct poller_timer date_chg_tmr; ///< Print a date change + struct poller_timer autoaway_tmr; ///< Autoaway timer struct poller poller; ///< Manages polled descriptors bool quitting; ///< User requested quitting @@ -1863,6 +1865,16 @@ static struct config_schema g_config_behaviour[] = .validate = config_validate_nonnegative, .default_ = "600" }, + { .name = "autoaway_message", + .comment = "Automated away message", + .type = CONFIG_ITEM_STRING, + .default_ = "\"I'm not here right now\"" }, + { .name = "autoaway_delay", + .comment = "Delay from the last keypress in seconds", + .type = CONFIG_ITEM_INTEGER, + .validate = config_validate_nonnegative, + .default_ = "1800" }, + { .name = "plugin_autoload", .comment = "Plugins to automatically load on start", .type = CONFIG_ITEM_STRING_ARRAY, @@ -6110,6 +6122,7 @@ irc_try_parse_welcome_for_userhost (struct server *s, const char *m) static bool process_input_utf8 (struct app_context *, struct buffer *, const char *, int); +static void on_autoaway_timer (struct app_context *ctx); static void irc_on_registered (struct server *s, const char *nickname) @@ -6124,6 +6137,10 @@ irc_on_registered (struct server *s, const char *nickname) // XXX: we can also use WHOIS if it's not supported (optional by RFC 2812) irc_send (s, "USERHOST %s", s->irc_user->nickname); + // A little hack that reinstates auto-away status when we get disconnected + if (s->autoaway_active) + on_autoaway_timer (s->ctx); + const char *command = get_config_string (s->config, "command"); if (command) { @@ -6486,6 +6503,24 @@ irc_handle_rpl_inviting (struct server *s, const struct irc_message *msg) "You have invited #n to #S", nickname, channel_name); } +static void +irc_handle_rpl_nowaway (struct server *s, const struct irc_message *msg) +{ + (void) msg; + + if (s->irc_user) + s->irc_user->away = true; +} + +static void +irc_handle_rpl_unaway (struct server *s, const struct irc_message *msg) +{ + (void) msg; + + if (s->irc_user) + s->irc_user->away = false; +} + static void irc_handle_err_nicknameinuse (struct server *s, const struct irc_message *msg) { @@ -6707,6 +6742,10 @@ irc_process_numeric (struct server *s, irc_handle_rpl_topicwhotime (s, msg); buffer = NULL; break; case IRC_RPL_INVITING: irc_handle_rpl_inviting (s, msg); buffer = NULL; break; + case IRC_RPL_NOWAWAY: + irc_handle_rpl_nowaway (s, msg); break; + case IRC_RPL_UNAWAY: + irc_handle_rpl_unaway (s, msg); break; case IRC_ERR_NICKNAMEINUSE: irc_handle_err_nicknameinuse (s, msg); buffer = NULL; break; @@ -10979,11 +11018,61 @@ done: ctx->in_bracketed_paste = false; } +static void +reset_autoaway (struct app_context *ctx) +{ + // Stop the last one if it's been disabled altogether in the meantime + poller_timer_reset (&ctx->autoaway_tmr); + + // Unset any automated statuses that are active right at this moment + struct str_map_iter iter; + str_map_iter_init (&iter, &ctx->servers); + struct server *s; + while ((s = str_map_iter_next (&iter))) + { + if (s->autoaway_active + && s->irc_user + && s->irc_user->away) + irc_send (s, "AWAY"); + + s->autoaway_active = false; + } + + // And potentially start a new auto-away timer + int64_t delay = get_config_integer + (ctx->config.root, "behaviour.autoaway_delay"); + if (delay) + poller_timer_set (&ctx->autoaway_tmr, delay * 1000); +} + +static void +on_autoaway_timer (struct app_context *ctx) +{ + // An empty message would unset any away status, so let's ignore that + const char *message = get_config_string + (ctx->config.root, "behaviour.autoaway_message"); + if (!message || !*message) + return; + + struct str_map_iter iter; + str_map_iter_init (&iter, &ctx->servers); + struct server *s; + while ((s = str_map_iter_next (&iter))) + { + // If the user has already been marked as away, + // don't override his current away status + if (s->irc_user + && s->irc_user->away) + continue; + + irc_send (s, "AWAY :%s", message); + s->autoaway_active = true; + } +} + static void on_tty_readable (const struct pollfd *fd, struct app_context *ctx) { - (void) ctx; - if (fd->revents & ~(POLLIN | POLLHUP | POLLERR)) print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents); @@ -10995,6 +11084,10 @@ on_tty_readable (const struct pollfd *fd, struct app_context *ctx) // XXX: this may loop for a bit: stop the event or eat the input? // (This prevents a segfault when the input has been stopped.) input_on_readable (&ctx->input); + + // User activity detected, stop current auto-away and start anew; + // since they might have just changed the settings, do this last + reset_autoaway (ctx); } static void @@ -11059,6 +11152,10 @@ init_poller_events (struct app_context *ctx) ctx->date_chg_tmr.dispatcher = (poller_timer_fn) on_date_change_timer; ctx->date_chg_tmr.user_data = ctx; rearm_date_change_timer (ctx); + + poller_timer_init (&ctx->autoaway_tmr, &ctx->poller); + ctx->autoaway_tmr.dispatcher = (poller_timer_fn) on_autoaway_timer; + ctx->autoaway_tmr.user_data = ctx; } // --- Main program ------------------------------------------------------------ @@ -11146,6 +11243,7 @@ main (int argc, char *argv[]) refresh_prompt (&ctx); input_start (&ctx.input, argv[0]); toggle_bracketed_paste (true); + reset_autoaway (&ctx); // Finally, we juice the configuration for some servers to create load_plugins (&ctx);