diff --git a/config/.gitignore b/config/.gitignore index f2ee7775..6b484efc 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -70,3 +70,5 @@ !coc !npm !zsh +!weechat +!weechat/** diff --git a/config/weechat/.gitignore b/config/weechat/.gitignore new file mode 100644 index 00000000..a5e0c4cf --- /dev/null +++ b/config/weechat/.gitignore @@ -0,0 +1,2 @@ +logs +logs/** diff --git a/config/weechat/alias.conf b/config/weechat/alias.conf new file mode 100644 index 00000000..0a02da3b --- /dev/null +++ b/config/weechat/alias.conf @@ -0,0 +1,47 @@ +# +# weechat -- alias.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[cmd] +AAWAY = "allserv /away" +ANICK = "allserv /nick" +BEEP = "print -beep" +BYE = "quit" +C = "buffer clear" +CHAT = "dcc chat" +CL = "buffer clear" +CLOSE = "buffer close" +EXIT = "quit" +IG = "ignore" +J = "join" +K = "kick" +KB = "kickban" +LEAVE = "part" +M = "msg" +MSGBUF = "command -buffer $1 * /input send $2-" +MUB = "unban *" +N = "names" +Q = "query" +REDRAW = "window refresh" +SAY = "msg *" +SIGNOFF = "quit" +T = "topic" +UB = "unban" +UMODE = "mode $nick" +V = "command core version" +W = "who" +WC = "window close" +WI = "whois" +WII = "whois $1 $1" +WM = "window merge" +WW = "whowas" + +[completion] +MSGBUF = "%(buffers_plugins_names)" diff --git a/config/weechat/autosort.conf b/config/weechat/autosort.conf new file mode 100644 index 00000000..fca216a9 --- /dev/null +++ b/config/weechat/autosort.conf @@ -0,0 +1,24 @@ +# +# weechat -- autosort.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[sorting] +case_sensitive = off +debug_log = off +replacements = "" +rules = "" +signal_delay = 5 +signals = "buffer_opened buffer_merged buffer_unmerged buffer_renamed" +sort_limit = 100 +sort_on_config_change = on + +[v3] +helpers = "{"core_first": "${if:${buffer.full_name}!=core.weechat}", "irc_raw_first": "${if:${buffer.full_name}!=irc.irc_raw}", "irc_raw_last": "${if:${buffer.full_name}==irc.irc_raw}", "hashless_name": "${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}", "script_or_plugin": "${if:${script_name}?${script_name}:${plugin}}"}" +rules = "["${core_first}", "${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}", "${script_or_plugin}", "${irc_raw_first}", "${server}", "${info:autosort_order,${type},server,*,channel,private}", "${hashless_name}", "${buffer.full_name}"]" diff --git a/config/weechat/buflist.conf b/config/weechat/buflist.conf new file mode 100644 index 00000000..225b5e86 --- /dev/null +++ b/config/weechat/buflist.conf @@ -0,0 +1,40 @@ +# +# weechat -- buflist.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +add_newline = on +auto_scroll = 50 +display_conditions = "${buffer.hidden}==0" +enabled = on +mouse_jump_visited_buffer = off +mouse_move_buffer = on +mouse_wheel = on +nick_prefix = off +nick_prefix_empty = on +signals_refresh = "" +sort = "number,-active" +use_items = 1 + +[format] +buffer = "${format_number}${indent}${format_nick_prefix}${color_hotlist}${format_name}" +buffer_current = "${color:,blue}${format_buffer}" +hotlist = " ${color:green}(${hotlist}${color:green})" +hotlist_highlight = "${color:magenta}" +hotlist_low = "${color:white}" +hotlist_message = "${color:brown}" +hotlist_none = "${color:default}" +hotlist_private = "${color:green}" +hotlist_separator = "${color:default}," +indent = " " +lag = " ${color:green}[${color:brown}${lag}${color:green}]" +name = "${name}" +nick_prefix = "${color_nick_prefix}${nick_prefix}" +number = "${color:green}${number}${if:${number_displayed}?.: }" diff --git a/config/weechat/charset.conf b/config/weechat/charset.conf new file mode 100644 index 00000000..00d304cd --- /dev/null +++ b/config/weechat/charset.conf @@ -0,0 +1,18 @@ +# +# weechat -- charset.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[default] +decode = "iso-8859-1" +encode = "" + +[decode] + +[encode] diff --git a/config/weechat/colorize_nicks.conf b/config/weechat/colorize_nicks.conf new file mode 100644 index 00000000..a3cf7589 --- /dev/null +++ b/config/weechat/colorize_nicks.conf @@ -0,0 +1,20 @@ +# +# weechat -- colorize_nicks.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +blacklist_channels = "" +blacklist_nicks = "so,root" +colorize_input = off +greedy_matching = on +ignore_nicks_in_urls = off +ignore_tags = "" +match_limit = 20 +min_nick_length = 2 diff --git a/config/weechat/exec.conf b/config/weechat/exec.conf new file mode 100644 index 00000000..9e1b4f1e --- /dev/null +++ b/config/weechat/exec.conf @@ -0,0 +1,19 @@ +# +# weechat -- exec.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[command] +default_options = "" +purge_delay = 0 +shell = "${env:SHELL}" + +[color] +flag_finished = lightred +flag_running = lightgreen diff --git a/config/weechat/fifo.conf b/config/weechat/fifo.conf new file mode 100644 index 00000000..904985df --- /dev/null +++ b/config/weechat/fifo.conf @@ -0,0 +1,14 @@ +# +# weechat -- fifo.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[file] +enabled = on +path = "%h/weechat_fifo" diff --git a/config/weechat/fset.conf b/config/weechat/fset.conf new file mode 100644 index 00000000..53b0da7e --- /dev/null +++ b/config/weechat/fset.conf @@ -0,0 +1,96 @@ +# +# weechat -- fset.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +auto_refresh = "*" +auto_unmark = off +condition_catch_set = "${count} >= 1" +export_help_default = on +format_number = 1 +marked_string = "*" +scroll_horizontal = 10 +show_plugins_desc = off +sort = "~name" +unmarked_string = " " +use_color_value = off +use_keys = on +use_mute = off + +[format] +export_help = "# ${description2}" +export_option = "/set ${name} ${quoted_value}" +export_option_null = "/unset ${name}" +option1 = "" +option2 = "${marked} ${name} ${type} ${value2}${newline} ${empty_name} ${_default_value}${color:darkgray} -- ${min}..${max}${newline} ${empty_name} ${description}" + +[color] +default_value = default +default_value_selected = white +description = default +description_selected = white +file = default +file_changed = brown +file_changed_selected = yellow +file_selected = white +help_default_value = white +help_description = default +help_name = white +help_quotes = darkgray +help_values = default +index = cyan +index_selected = lightcyan +line_marked_bg1 = default +line_marked_bg2 = default +line_selected_bg1 = blue +line_selected_bg2 = red +marked = brown +marked_selected = yellow +max = default +max_selected = white +min = default +min_selected = white +name = default +name_changed = brown +name_changed_selected = yellow +name_selected = white +option = default +option_changed = brown +option_changed_selected = yellow +option_selected = white +parent_name = default +parent_name_selected = white +parent_value = cyan +parent_value_selected = lightcyan +quotes = darkgray +quotes_changed = default +quotes_changed_selected = white +quotes_selected = default +section = default +section_changed = brown +section_changed_selected = yellow +section_selected = white +string_values = default +string_values_selected = white +title_count_options = cyan +title_current_option = lightcyan +title_filter = yellow +title_marked_options = lightgreen +title_sort = white +type = green +type_selected = lightgreen +unmarked = default +unmarked_selected = white +value = cyan +value_changed = brown +value_changed_selected = yellow +value_selected = lightcyan +value_undef = magenta +value_undef_selected = lightmagenta diff --git a/config/weechat/guile.conf b/config/weechat/guile.conf new file mode 100644 index 00000000..07a4910e --- /dev/null +++ b/config/weechat/guile.conf @@ -0,0 +1,14 @@ +# +# weechat -- guile.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +check_license = off +eval_keep_context = on diff --git a/config/weechat/irc.conf b/config/weechat/irc.conf new file mode 100644 index 00000000..fc18feb2 --- /dev/null +++ b/config/weechat/irc.conf @@ -0,0 +1,205 @@ +# +# weechat -- irc.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +buffer_open_before_autojoin = on +buffer_open_before_join = off +buffer_switch_autojoin = on +buffer_switch_join = on +color_nicks_in_names = off +color_nicks_in_nicklist = off +color_nicks_in_server_messages = on +color_pv_nick_like_channel = on +ctcp_time_format = "%a, %d %b %Y %T %z" +display_away = local +display_ctcp_blocked = on +display_ctcp_reply = on +display_ctcp_unknown = on +display_host_join = on +display_host_join_local = on +display_host_quit = on +display_join_message = "329,332,333,366" +display_old_topic = on +display_pv_away_once = on +display_pv_back = on +display_pv_warning_address = off +highlight_channel = "$nick" +highlight_pv = "$nick" +highlight_server = "$nick" +highlight_tags_restrict = "irc_privmsg,irc_notice" +item_channel_modes_hide_args = "k" +item_display_server = buffer_plugin +item_nick_modes = on +item_nick_prefix = on +join_auto_add_chantype = off +msgbuffer_fallback = current +new_channel_position = none +new_pv_position = none +nick_completion_smart = speakers +nick_mode = prefix +nick_mode_empty = off +nicks_hide_password = "nickserv" +notice_as_pv = auto +notice_welcome_redirect = on +notice_welcome_tags = "" +notify_tags_ison = "notify_message" +notify_tags_whois = "notify_message" +part_closes_buffer = off +pv_buffer = independent +pv_tags = "notify_private" +raw_messages = 256 +server_buffer = merge_with_core +smart_filter = on +smart_filter_account = on +smart_filter_chghost = on +smart_filter_delay = 5 +smart_filter_join = on +smart_filter_join_unmask = 30 +smart_filter_mode = "+" +smart_filter_nick = on +smart_filter_quit = on +temporary_servers = off +topic_strip_colors = off + +[color] +input_nick = lightcyan +item_channel_modes = default +item_lag_counting = default +item_lag_finished = yellow +item_nick_modes = default +message_account = cyan +message_chghost = brown +message_join = green +message_kick = red +message_quit = red +mirc_remap = "1,-1:darkgray" +nick_prefixes = "y:lightred;q:lightred;a:lightcyan;o:lightgreen;h:lightmagenta;v:yellow;*:lightblue" +notice = green +reason_kick = default +reason_quit = default +topic_current = default +topic_new = white +topic_old = default + +[network] +autoreconnect_delay_growing = 2 +autoreconnect_delay_max = 600 +ban_mask_default = "*!$ident@$host" +colors_receive = on +colors_send = on +lag_check = 60 +lag_max = 1800 +lag_min_show = 500 +lag_reconnect = 300 +lag_refresh_interval = 1 +notify_check_ison = 1 +notify_check_whois = 5 +sasl_fail_unavailable = on +send_unknown_commands = off +whois_double_nick = off + +[msgbuffer] + +[ctcp] + +[ignore] + +[server_default] +addresses = "" +anti_flood_prio_high = 2 +anti_flood_prio_low = 2 +autoconnect = off +autojoin = "" +autoreconnect = on +autoreconnect_delay = 10 +autorejoin = off +autorejoin_delay = 30 +away_check = 0 +away_check_max_nicks = 25 +capabilities = "" +charset_message = message +command = "" +command_delay = 0 +connection_timeout = 60 +default_chantypes = "#&" +ipv6 = on +local_hostname = "" +msg_kick = "" +msg_part = "WeeChat ${info:version}" +msg_quit = "WeeChat ${info:version}" +nicks = "yigit,yigit1,yigit2,yigit3,yigit4" +nicks_alternate = on +notify = "" +password = "" +proxy = "" +realname = "" +sasl_fail = continue +sasl_key = "" +sasl_mechanism = plain +sasl_password = "" +sasl_timeout = 15 +sasl_username = "" +split_msg_max_length = 512 +ssl = off +ssl_cert = "" +ssl_dhkey_size = 2048 +ssl_fingerprint = "" +ssl_password = "" +ssl_priorities = "NORMAL:-VERS-SSL3.0" +ssl_verify = on +usermode = "" +username = "yigit" + +[server] +freenode.addresses = "chat.freenode.net/6697" +freenode.proxy +freenode.ipv6 +freenode.ssl = on +freenode.ssl_cert +freenode.ssl_password +freenode.ssl_priorities +freenode.ssl_dhkey_size +freenode.ssl_fingerprint +freenode.ssl_verify +freenode.password +freenode.capabilities +freenode.sasl_mechanism = plain +freenode.sasl_username = "Fr1nge" +freenode.sasl_password = "${sec.data.freenode_password}" +freenode.sasl_key +freenode.sasl_timeout +freenode.sasl_fail +freenode.autoconnect = on +freenode.autoreconnect +freenode.autoreconnect_delay +freenode.nicks = "Fr1nge" +freenode.nicks_alternate +freenode.username = "Fr1nge" +freenode.realname = "Yigit Colakoglu" +freenode.local_hostname +freenode.usermode +freenode.command +freenode.command_delay +freenode.autojoin = "#vim,#archlinux,#lf,##C,#python,##java" +freenode.autorejoin +freenode.autorejoin_delay +freenode.connection_timeout +freenode.anti_flood_prio_high +freenode.anti_flood_prio_low +freenode.away_check +freenode.away_check_max_nicks +freenode.msg_kick +freenode.msg_part +freenode.msg_quit +freenode.notify +freenode.split_msg_max_length +freenode.charset_message +freenode.default_chantypes diff --git a/config/weechat/logger.conf b/config/weechat/logger.conf new file mode 100644 index 00000000..d0d3218d --- /dev/null +++ b/config/weechat/logger.conf @@ -0,0 +1,36 @@ +# +# weechat -- logger.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +backlog = 20 +backlog_conditions = "" + +[color] +backlog_end = default +backlog_line = default + +[file] +auto_log = on +color_lines = off +flush_delay = 120 +fsync = off +info_lines = off +mask = "$plugin.$name.weechatlog" +name_lower_case = on +nick_prefix = "" +nick_suffix = "" +path = "%h/logs/" +replacement_char = "_" +time_format = "%Y-%m-%d %H:%M:%S" + +[level] + +[mask] diff --git a/config/weechat/lua.conf b/config/weechat/lua.conf new file mode 100644 index 00000000..fa4966b2 --- /dev/null +++ b/config/weechat/lua.conf @@ -0,0 +1,14 @@ +# +# weechat -- lua.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +check_license = off +eval_keep_context = on diff --git a/config/weechat/perl.conf b/config/weechat/perl.conf new file mode 100644 index 00000000..31924b91 --- /dev/null +++ b/config/weechat/perl.conf @@ -0,0 +1,14 @@ +# +# weechat -- perl.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +check_license = off +eval_keep_context = on diff --git a/config/weechat/perl/atcomplete.pl b/config/weechat/perl/atcomplete.pl new file mode 100644 index 00000000..e0b5d5c8 --- /dev/null +++ b/config/weechat/perl/atcomplete.pl @@ -0,0 +1,89 @@ +# Copyright 2015 by David A. Golden. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# ABOUT +# +# atcomplete.pl +# +# Adds nick completion when prefixed with '@' for use with IRC gateways +# for Slack, Flowdock, etc. as these require the '@' to highlight users +# +# CONFIG +# +# /set plugins.var.perl.atcomplete.enabled +# +# HISTORY +# +# 0.001 -- xdg, 2016-04-06 +# +# - initial release +# +# REPOSITORY +# +# https://github.com/xdg/weechat-atcomplete + +use strict; +use warnings; +my $SCRIPT_NAME = "atcomplete"; +my $VERSION = "0.001"; + +my %options_default = ( + 'enabled' => ['on', 'enable completion of nicks starting with @'], +); +my %options = (); + +weechat::register($SCRIPT_NAME, "David A. Golden", $VERSION, + "Apache2", "atcomplete - do nick completion following @", "", ""); +init_config(); + +weechat::hook_config("plugins.var.perl.$SCRIPT_NAME.*", "toggle_config_by_set", ""); +weechat::hook_completion("nicks", "Add @ prefix to nick completion", "complete_at_nicks", ""); + +sub complete_at_nicks { + my ($data, $completion_item, $buffer, $completion ) = @_; + return weechat::WEECHAT_RC_OK() unless $options{enabled} eq 'on'; + + my $nicklist = weechat::infolist_get("nicklist", weechat::current_buffer(), ""); + + if ($nicklist ne "") { + while (weechat::infolist_next($nicklist)) { + next unless weechat::infolist_string($nicklist, "type") eq "nick"; + my $nick = weechat::infolist_string($nicklist, "name"); + weechat::hook_completion_list_add($completion, "\@$nick", 1, weechat::WEECHAT_LIST_POS_SORT()); + } + } + + weechat::infolist_free($nicklist); + + return weechat::WEECHAT_RC_OK(); +} + +sub toggle_config_by_set { + my ($pointer, $name, $value) = @_; + $name = substr($name, length("plugins.var.perl.".$SCRIPT_NAME."."), length($name)); + $options{$name} = $value; + return weechat::WEECHAT_RC_OK(); +} + +sub init_config { + my $version = weechat::info_get("version_number", "") || 0; + foreach my $option (keys %options_default) + { + if (!weechat::config_is_set_plugin($option)) + { + weechat::config_set_plugin($option, $options_default{$option}[0]); + $options{$option} = $options_default{$option}[0]; + } + else + { + $options{$option} = weechat::config_get_plugin($option); + } + if ($version >= 0x00030500) + { + weechat::config_set_desc_plugin($option, $options_default{$option}[1]." (default: \"".$options_default{$option}[0]."\")"); + } + } +} diff --git a/config/weechat/perl/autoload/atcomplete.pl b/config/weechat/perl/autoload/atcomplete.pl new file mode 120000 index 00000000..bd3e9ea3 --- /dev/null +++ b/config/weechat/perl/autoload/atcomplete.pl @@ -0,0 +1 @@ +../atcomplete.pl \ No newline at end of file diff --git a/config/weechat/perl/autoload/highmon.pl b/config/weechat/perl/autoload/highmon.pl new file mode 120000 index 00000000..2eb5e1e5 --- /dev/null +++ b/config/weechat/perl/autoload/highmon.pl @@ -0,0 +1 @@ +../highmon.pl \ No newline at end of file diff --git a/config/weechat/perl/autoload/multiline.pl b/config/weechat/perl/autoload/multiline.pl new file mode 120000 index 00000000..cf9fe28f --- /dev/null +++ b/config/weechat/perl/autoload/multiline.pl @@ -0,0 +1 @@ +../multiline.pl \ No newline at end of file diff --git a/config/weechat/perl/highmon.pl b/config/weechat/perl/highmon.pl new file mode 100644 index 00000000..f843cade --- /dev/null +++ b/config/weechat/perl/highmon.pl @@ -0,0 +1,1154 @@ +# +# highmon.pl - Highlight Monitoring for weechat 0.3.0 +# +# Add 'Highlight Monitor' buffer/bar to log all highlights in one spot +# +# Usage: +# /highmon [help] | [monitor [channel [server]]] | [clean default|orphan|all] | clearbar +# Command wrapper for highmon commands +# +# /highmon clean default|orphan|all will clean the config section of default 'on' entries, +# channels you are no longer joined, or both +# +# /highmon clearbar will clear the contents of highmon's bar output +# +# /highmon monitor [channel] [server] is used to toggle a highlight monitoring on and off, this +# can be used in the channel buffer for the channel you wish to toggle, or be given +# with arguments e.g. /highmon monitor #weechat freenode +# +# /set plugins.var.perl.highmon.alignment +# The config setting "alignment" can be changed to; +# "channel", "schannel", "nchannel", "channel,nick", "schannel,nick", "nchannel,nick" +# to change how the monitor appears +# The 'channel' value will show: "#weechat" +# The 'schannel' value will show: "6" +# The 'nchannel' value will show: "6:#weechat" +# +# /set plugins.var.perl.highmon.short_names +# Setting this to 'on' will trim the network name from highmon, ala buffers.pl +# +# /set plugins.var.perl.highmon.merge_private +# Setting this to 'on' will merge private messages to highmon's display +# +# /set plugins.var.perl.highmon.color_buf +# This turns colored buffer names on or off, you can also set a single fixed color by using a weechat color name. +# This *must* be a valid color name, or weechat will likely do unexpected things :) +# +# /set plugins.var.perl.highmon.hotlist_show +# Setting this to 'on' will let the highmon buffer appear in hotlists +# (status bar/buffer.pl) +# +# /set plugins.var.perl.highmon.away_only +# Setting this to 'on' will only put messages in the highmon buffer when +# you set your status to away +# +# /set plugins.var.perl.highmon.logging +# Toggles logging status for highmon buffer (default: off) +# +# /set plugins.var.perl.highmon.output +# Changes where output method of highmon; takes either "bar" or "buffer" (default; buffer) +# /set plugins.var.perl.highmon.bar_lines +# Changes the amount of lines the output bar will hold. +# (Only appears once output has been set to bar, defaults to 10) +# /set plugins.var.perl.highmon.bar_scrolldown +# Toggles the bar scrolling at the bottom when new highlights are received +# (Only appears once output has been set to bar, defaults to off) +# +# /set plugins.var.perl.highmon.nick_prefix +# /set plugins.var.perl.highmon.nick_suffix +# Sets the prefix and suffix chars in the highmon buffer +# (Defaults to <> if nothing set, and blank if there is) +# +# servername.#channel +# servername is the internal name for the server (set when you use /server add) +# #channel is the channel name, (where # is whatever channel type that channel happens to be) +# +# Optional, set up tweaks; Hide the status and input lines on highmon +# +# /set weechat.bar.status.conditions "${window.buffer.full_name} != perl.highmon" +# /set weechat.bar.input.conditions "${window.buffer.full_name} != perl.highmon" +# + +# Bugs and feature requests at: https://github.com/KenjiE20/highmon + +# History: +# 2020-06-21, Sebastien Helleu : +# v2.7: make call to bar_new compatible with WeeChat >= 2.9 +# 2019-05-13, HubbeKing +# v2.6: -add: send "logger_backlog" signal on buffer open if logging is enabled +# 2014-08-16, KenjiE20 : +# v2.5: -add: clearbar command to clear bar output +# -add: firstrun output prompt to check the help text for set up hints as they were being missed +# and update hint for conditions to use eval +# -change: Make all outputs use the date callback for more accurate timestamps (thanks Germainz) +# 2013-12-04, KenjiE20 : +# v2.4: -add: Support for eval style colour codes in time format used for bar output +# 2013-10-22, KenjiE20 : +# v2.3.3.2: -fix: Typo in fix command +# 2013-10-10, KenjiE20 : +# v2.3.3.1: -fix: Typo in closed buffer warning +# 2013-10-07, KenjiE20 : +# v2.3.3: -add: Warning and fixer for accidental buffer closes +# 2013-01-15, KenjiE20 : +# v2.3.2: -fix: Let bar output use the string set in weechat's config option +# -add: github info +# 2012-04-15, KenjiE20 : +# v2.3.1: -fix: Colour tags in bar timestamp string +# 2012-02-28, KenjiE20 : +# v2.3: -feature: Added merge_private option to display private messages (default: off) +# -fix: Channel name colours now show correctly when set to on +# 2011-08-07, Sitaktif : +# v2.2.1: -feature: Add "bar_scrolldown" option to have the bar display the latest hl at anytime +# -fix: Set up bar-specific config at startup if 'output' is already configured as 'bar' +# 2010-12-22, KenjiE20 : +# v2.2: -change: Use API instead of config to find channel colours, ready for 0.3.4 and 256 colours +# 2010-12-13, idl0r & KenjiE20 : +# v2.1.3: -fix: perl errors caused by bar line counter +# -fix: Add command list to inbuilt help +# 2010-09-30, KenjiE20 : +# v2.1.2: -fix: logging config was not correctly toggling back on (thanks to sleo for noticing) +# -version sync w/ chanmon +# 2010-08-27, KenjiE20 : +# v2.1: -feature: Add 'nchannel' option to alignment to display buffer and name +# 2010-04-25, KenjiE20 : +# v2.0: Release as version 2.0 +# 2010-04-24, KenjiE20 : +# v1.9: Rewrite for v2.0 +# Bring feature set in line with chanmon 2.0 +# -code change: Made more subs to shrink the code down in places +# -fix: Stop highmon attempting to double load/hook +# -fix: Add version dependant check for away status +# 2010-01-25, KenjiE20 : +# v1.7: -fixture: Let highmon be aware of nick_prefix/suffix +# and allow custom prefix/suffix for chanmon buffer +# (Defaults to <> if nothing set, and blank if there is) +# (Thanks to m4v for these) +# 2009-09-07, KenjiE20 : +# v1.6: -feature: colored buffer names +# -change: version sync with chanmon +# 2009-09-05, KenjiE20 : +# v1.2: -fix: disable buffer highlight +# 2009-09-02, KenjiE20 : +# v.1.1.1 -change: Stop unsightly text block on '/help' +# 2009-08-10, KenjiE20 : +# v1.1: In-client help added +# 2009-08-02, KenjiE20 : +# v1.0: Initial Public Release + +# Copyright (c) 2009 by KenjiE20 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +@bar_lines = (); +@bar_lines_time = (); +# Replicate info earlier for in-client help + +$highmonhelp = weechat::color("bold")."/highmon [help] | [monitor [channel [server]]] | [clean default|orphan|all] | clearbar".weechat::color("-bold")." + Command wrapper for highmon commands + +".weechat::color("bold")."/highmon clean default|orphan|all".weechat::color("-bold")." will clean the config section of default 'on' entries, channels you are no longer joined, or both + +".weechat::color("bold")."/highmon clearbar".weechat::color("-bold")." will clear the contents of highmon's bar output + +".weechat::color("bold")."/highmon monitor [channel] [server]".weechat::color("-bold")." is used to toggle a highlight monitoring on and off, this can be used in the channel buffer for the channel you wish to toggle, or be given with arguments e.g. /highmon monitor #weechat freenode + +".weechat::color("bold")."/set plugins.var.perl.highmon.alignment".weechat::color("-bold")." + The config setting \"alignment\" can be changed to; + \"channel\", \"schannel\", \"nchannel\", \"channel,nick\", \"schannel,nick\", \"nchannel,nick\" + to change how the monitor appears + The 'channel' value will show: \"#weechat\" + The 'schannel' value will show: \"6\" + The 'nchannel' value will show: \"6:#weechat\" + +".weechat::color("bold")."/set plugins.var.perl.highmon.short_names".weechat::color("-bold")." + Setting this to 'on' will trim the network name from highmon, ala buffers.pl + +".weechat::color("bold")."/set plugins.var.perl.highmon.merge_private".weechat::color("-bold")." + Setting this to 'on' will merge private messages to highmon's display + +".weechat::color("bold")."/set plugins.var.perl.highmon.color_buf".weechat::color("-bold")." + This turns colored buffer names on or off, you can also set a single fixed color by using a weechat color name. + This ".weechat::color("bold")."must".weechat::color("-bold")." be a valid color name, or weechat will likely do unexpected things :) + +".weechat::color("bold")."/set plugins.var.perl.highmon.hotlist_show".weechat::color("-bold")." +Setting this to 'on' will let the highmon buffer appear in hotlists (status bar/buffer.pl) + +".weechat::color("bold")."/set plugins.var.perl.highmon.away_only".weechat::color("-bold")." +Setting this to 'on' will only put messages in the highmon buffer when you set your status to away + +".weechat::color("bold")."/set plugins.var.perl.highmon.logging".weechat::color("-bold")." + Toggles logging status for highmon buffer (default: off) + +".weechat::color("bold")."/set plugins.var.perl.highmon.output".weechat::color("-bold")." + Changes where output method of highmon; takes either \"bar\" or \"buffer\" (default; buffer) +".weechat::color("bold")."/set plugins.var.perl.highmon.bar_lines".weechat::color("-bold")." + Changes the amount of lines the output bar will hold. + (Only appears once output has been set to bar, defaults to 10) +".weechat::color("bold")."/set plugins.var.perl.highmon.bar_scrolldown".weechat::color("-bold")." + Toggles the bar scrolling at the bottom when new highlights are received + (Only appears once output has been set to bar, defaults to off) + +".weechat::color("bold")."/set plugins.var.perl.highmon.nick_prefix".weechat::color("-bold")." +".weechat::color("bold")."/set plugins.var.perl.highmon.nick_suffix".weechat::color("-bold")." + Sets the prefix and suffix chars in the highmon buffer + (Defaults to <> if nothing set, and blank if there is) + +".weechat::color("bold")."servername.#channel".weechat::color("-bold")." + servername is the internal name for the server (set when you use /server add) + #channel is the channel name, (where # is whatever channel type that channel happens to be) + +".weechat::color("bold")."Optional, set up tweaks;".weechat::color("-bold")." Hide the status and input lines on highmon + +".weechat::color("bold")."/set weechat.bar.status.conditions \"\${window.buffer.full_name} != perl.highmon\"".weechat::color("-bold")." +".weechat::color("bold")."/set weechat.bar.input.conditions \"\${window.buffer.full_name} != perl.highmon\"".weechat::color("-bold"); +# Print verbose help +sub print_help +{ + weechat::print("", "\t".weechat::color("bold")."Highmon Help".weechat::color("-bold")."\n\n"); + weechat::print("", "\t".$highmonhelp); + return weechat::WEECHAT_RC_OK; +} + +# Bar item build +sub highmon_bar_build +{ + # Get max lines + $max_lines = weechat::config_get_plugin("bar_lines"); + $max_lines = $max_lines ? $max_lines : 10; + $str = ''; + $align_num = 0; + $count = 0; + # Keep lines within max + while ($#bar_lines > $max_lines) + { + shift(@bar_lines); + shift(@bar_lines_time); + } + # So long as we have some lines, build a string + if (@bar_lines) + { + # Build loop + $sep = " ".weechat::config_string(weechat::config_get("weechat.look.prefix_suffix"))." "; + foreach(@bar_lines) + { + # Find max align needed + $prefix_num = (index(weechat::string_remove_color($_, ""), $sep)); + $align_num = $prefix_num if ($prefix_num > $align_num); + } + foreach(@bar_lines) + { + # Get align for this line + $prefix_num = (index(weechat::string_remove_color($_, ""), $sep)); + + # Make string + $str = $str.$bar_lines_time[$count]." ".(" " x ($align_num - $prefix_num)).$_."\n"; + # Increment count for sync with time list + $count++; + } + } + return $str; +} + +# Make a new bar +sub highmon_bar_open +{ + # Make the bar item + weechat::bar_item_new("highmon", "highmon_bar_build", ""); + + if (weechat::info_get("version_number", "") >= 0x02090000) + { + $highmon_bar = weechat::bar_new ("highmon", "off", 100, "root", "", "bottom", "vertical", "vertical", 0, 0, "default", "cyan", "default", "default", "on", "highmon"); + } + else + { + $highmon_bar = weechat::bar_new ("highmon", "off", 100, "root", "", "bottom", "vertical", "vertical", 0, 0, "default", "cyan", "default", "on", "highmon"); + } + + return weechat::WEECHAT_RC_OK; +} +# Close bar +sub highmon_bar_close +{ + # Find if bar exists + $highmon_bar = weechat::bar_search("highmon"); + # If is does, close it + if ($highmon_bar ne "") + { + weechat::bar_remove($highmon_bar); + } + + # Find if bar item exists + $highmon_bar_item = weechat::bar_item_search("highmon_bar"); + # If is does, close it + if ($highmon_bar_item ne "") + { + weechat::bar_remove($highmon_bar_item); + } + + @bar_lines = (); + return weechat::WEECHAT_RC_OK; +} + +# Make a new buffer +sub highmon_buffer_open +{ + # Search for pre-existing buffer + $highmon_buffer = weechat::buffer_search("perl", "highmon"); + + # Make a new buffer + if ($highmon_buffer eq "") + { + $highmon_buffer = weechat::buffer_new("highmon", "highmon_buffer_input", "", "highmon_buffer_close", ""); + } + + # Turn off notify, highlights + if ($highmon_buffer ne "") + { + if (weechat::config_get_plugin("hotlist_show") eq "off") + { + weechat::buffer_set($highmon_buffer, "notify", "0"); + } + weechat::buffer_set($highmon_buffer, "highlight_words", "-"); + weechat::buffer_set($highmon_buffer, "title", "Highlight Monitor"); + # Set no_log + if (weechat::config_get_plugin("logging") eq "off") + { + weechat::buffer_set($highmon_buffer, "localvar_set_no_log", "1"); + } + # send "logger_backlog" signal if logging is enabled to display backlog + if (weechat::config_get_plugin("logging") eq "on") + { + weechat::hook_signal_send("logger_backlog", weechat::WEECHAT_HOOK_SIGNAL_POINTER, $highmon_buffer) + } + } + return weechat::WEECHAT_RC_OK; +} +# Buffer input has no action +sub highmon_buffer_input +{ + return weechat::WEECHAT_RC_OK; +} +# Close up +sub highmon_buffer_close +{ + $highmon_buffer = ""; + # If user hasn't changed output style warn user + if (weechat::config_get_plugin("output") eq "buffer") + { + weechat::print("", "\tHighmon buffer has been closed but output is still set to buffer, unusual results may occur. To recreate the buffer use ".weechat::color("bold")."/highmon fix".weechat::color("-bold")); + } + return weechat::WEECHAT_RC_OK; +} + +# Highmon command wrapper +sub highmon_command_cb +{ + $data = $_[0]; + $buffer = $_[1]; + $args = $_[2]; + my $cmd = ''; + my $arg = ''; + + if ($args ne "") + { + # Split argument up + @arg_array = split(/ /,$args); + # Take first as command + $cmd = shift(@arg_array); + # Rebuild string to pass to subs + if (@arg_array) + { + $arg = join(" ", @arg_array); + } + } + + # Help command + if ($cmd eq "" || $cmd eq "help") + { + print_help(); + } + # /monitor command + elsif ($cmd eq "monitor") + { + highmon_toggle($data, $buffer, $arg); + } + # /highclean command + elsif ($cmd eq "clean") + { + highmon_config_clean($data, $buffer, $arg); + } + # clearbar command + elsif ($cmd eq "clearbar") + { + if (weechat::config_get_plugin("output") eq "bar") + { + @bar_lines = (); + weechat::bar_item_update("highmon"); + } + } + # Fix closed buffer + elsif ($cmd eq "fix") + { + if (weechat::config_get_plugin("output") eq "buffer" && $highmon_buffer eq "") + { + highmon_buffer_open(); + } + } + return weechat::WEECHAT_RC_OK; +} + +# Clean up config entries +sub highmon_config_clean +{ + $data = $_[0]; + $buffer = $_[1]; + $args = $_[2]; + + # Don't do anything if bad option given + if ($args ne "default" && $args ne "orphan" && $args ne "all") + { + weechat::print("", "\thighmon.pl: Unknown option"); + return weechat::WEECHAT_RC_OK; + } + + @chans = (); + # Load an infolist of highmon options + $infolist = weechat::infolist_get("option", "", "*highmon*"); + while (weechat::infolist_next($infolist)) + { + $name = weechat::infolist_string($infolist, "option_name"); + $name =~ s/perl\.highmon\.(\w*)\.([#&\+!])(.*)/$1.$2$3/; + if ($name =~ /^(.*)\.([#&\+!])(.*)$/) + { + $action = 0; + # Clean up all 'on's + if ($args eq "default" || $args eq "all") + { + # If value in config is "on" + if (weechat::config_get_plugin($name) eq "on") + { + # Unset and if successful flag as changed + $rc = weechat::config_unset_plugin($name); + if ($rc eq weechat::WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED) + { + $action = 1; + } + } + } + # Clean non joined + if ($args eq "orphan" || $args eq "all") + { + # If we can't find the buffer for this entry + if (weechat::buffer_search("irc", $name) eq "") + { + # Unset and if successful flag as changed + $rc = weechat::config_unset_plugin($name); + if ($rc eq weechat::WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED) + { + $action = 1; + } + } + } + # Add changed entry names to list + push (@chans, $name) if ($action); + } + } + weechat::infolist_free($infolist); + # If channels were cleaned from config + if (@chans) + { + # If only one entry + if (@chans == 1) + { + $str = "\thighmon.pl: Cleaned ".@chans." entry from the config:"; + } + else + { + $str = "\thighmon.pl: Cleaned ".@chans." entries from the config:"; + } + # Build a list of channels + foreach(@chans) + { + $str = $str." ".$_; + } + # Print what happened + weechat::print("",$str); + } + # Config seemed to be clean + else + { + weechat::print("", "\thighmon.pl: No entries removed"); + } + return weechat::WEECHAT_RC_OK; +} + +# Check config elements +sub highmon_config_init +{ + # First run default + if (!(weechat::config_is_set_plugin ("first_run"))) + { + if (weechat::config_get_plugin("first_run") ne "true") + { + weechat::print("", "\tThis appears to be the first time highmon has been run. For help and common set up hints see /highmon help"); + weechat::config_set_plugin("first_run", "true"); + } + } + # Alignment default + if (!(weechat::config_is_set_plugin ("alignment"))) + { + weechat::config_set_plugin("alignment", "channel"); + } + if (weechat::config_get_plugin("alignment") eq "") + { + weechat::config_set_plugin("alignment", "none"); + } + + # Short name default + if (!(weechat::config_is_set_plugin ("short_names"))) + { + weechat::config_set_plugin("short_names", "off"); + } + + # Coloured names default + if (!(weechat::config_is_set_plugin ("color_buf"))) + { + weechat::config_set_plugin("color_buf", "on"); + } + + # Hotlist show default + if (!(weechat::config_is_set_plugin ("hotlist_show"))) + { + weechat::config_set_plugin("hotlist_show", "off"); + } + + # Away only default + if (!(weechat::config_is_set_plugin ("away_only"))) + { + weechat::config_set_plugin("away_only", "off"); + } + + # highmon log default + if (!(weechat::config_is_set_plugin ("logging"))) + { + weechat::config_set_plugin("logging", "off"); + } + + # Output default + if (!(weechat::config_is_set_plugin ("output"))) + { + weechat::config_set_plugin("output", "buffer"); + } + + # Private message merging + if (!(weechat::config_is_set_plugin ("merge_private"))) + { + weechat::config_set_plugin("merge_private", "off"); + } + + # Set bar config in case output was set to "bar" before even changing the setting + if (weechat::config_get_plugin("output") eq "bar") + { + # Output bar lines default + if (!(weechat::config_is_set_plugin ("bar_lines"))) + { + weechat::config_set_plugin("bar_lines", "10"); + } + if (!(weechat::config_is_set_plugin ("bar_scrolldown"))) + { + weechat::config_set_plugin("bar_scrolldown", "off"); + } + } + + # Check for exisiting prefix/suffix chars, and setup accordingly + $prefix = weechat::config_get("irc.look.nick_prefix"); + $prefix = weechat::config_string($prefix); + $suffix = weechat::config_get("irc.look.nick_suffix"); + $suffix = weechat::config_string($suffix); + + if (!(weechat::config_is_set_plugin("nick_prefix"))) + { + if ($prefix eq "" && $suffix eq "") + { + weechat::config_set_plugin("nick_prefix", "<"); + } + else + { + weechat::config_set_plugin("nick_prefix", ""); + } + } + + if (!(weechat::config_is_set_plugin("nick_suffix"))) + { + if ($prefix eq "" && $suffix eq "") + { + weechat::config_set_plugin("nick_suffix", ">"); + } + else + { + weechat::config_set_plugin("nick_suffix", ""); + } + } +} + +# Get config updates +sub highmon_config_cb +{ + $point = $_[0]; + $name = $_[1]; + $value = $_[2]; + + $name =~ s/^plugins\.var\.perl\.highmon\.//; + + # Set logging on buffer + if ($name eq "logging") + { + # Search for pre-existing buffer + $highmon_buffer = weechat::buffer_search("perl", "highmon"); + if ($value eq "off") + { + weechat::buffer_set($highmon_buffer, "localvar_set_no_log", "1"); + } + else + { + weechat::buffer_set($highmon_buffer, "localvar_del_no_log", ""); + } + } + # Output changer + elsif ($name eq "output") + { + if ($value eq "bar") + { + # Search for pre-existing buffer + $highmon_buffer = weechat::buffer_search("perl", "highmon"); + # Close if it exists + if ($highmon_buffer ne "") + { + weechat::buffer_close($highmon_buffer) + } + + # Output bar lines default + if (!(weechat::config_is_set_plugin ("bar_lines"))) + { + weechat::config_set_plugin("bar_lines", "10"); + } + if (!(weechat::config_is_set_plugin ("bar_scrolldown"))) + { + weechat::config_set_plugin("bar_scrolldown", "off"); + } + # Make a bar if doesn't exist + highmon_bar_open(); + } + elsif ($value eq "buffer") + { + # If a bar exists, close it + highmon_bar_close(); + # Open buffer + highmon_buffer_open(); + } + + } + # Change if hotlist config changes + elsif ($name eq "hotlist_show") + { + # Search for pre-existing buffer + $highmon_buffer = weechat::buffer_search("perl", "highmon"); + if ($value eq "off" && $highmon_buffer) + { + weechat::buffer_set($highmon_buffer, "notify", "0"); + } + elsif ($value ne "off" && $highmon_buffer) + { + weechat::buffer_set($highmon_buffer, "notify", "3"); + } + } + elsif ($name eq "weechat.look.prefix_suffix") + { + if (weechat::config_get_plugin("output") eq "bar") + { + @bar_lines = (); + weechat::print("", "\thighmon: weechat.look.prefix_suffix changed, clearing highmon bar"); + weechat::bar_item_update("highmon"); + } + } + return weechat::WEECHAT_RC_OK; +} + +# Set up weechat hooks / commands +sub highmon_hook +{ + weechat::hook_print("", "", "", 0, "highmon_new_message", ""); + weechat::hook_command("highclean", "Highmon config clean up", "default|orphan|all", " default: Cleans all config entries with the default \"on\" value\n orphan: Cleans all config entries for channels you aren't currently joined\n all: Does both defaults and orphan", "default|orphan|all", "highmon_config_clean", ""); + + weechat::hook_command("highmon", "Highmon help", "[help] | [monitor [channel [server]]] | [clean default|orphan|all] | clearbar", " help: Print help on config options for highmon\n monitor: Toggles monitoring for a channel\n clean: Highmon config clean up (/highclean)\nclearbar: Clear Highmon bar", "help || monitor %(irc_channels) %(irc_servers) || clean default|orphan|all || clearbar", "highmon_command_cb", ""); + + weechat::hook_config("plugins.var.perl.highmon.*", "highmon_config_cb", ""); + weechat::hook_config("weechat.look.prefix_suffix", "highmon_config_cb", ""); +} + +# Main body, Callback for hook_print +sub highmon_new_message +{ + my $net = ""; + my $chan = ""; + my $nick = ""; + my $outstr = ""; + my $window_displayed = ""; + my $dyncheck = "0"; + +# DEBUG point +# $string = "\t"."0: ".$_[0]." 1: ".$_[1]." 2: ".$_[2]." 3: ".$_[3]." 4: ".$_[4]." 5: ".$_[5]." 6: ".$_[6]." 7: ".$_[7]; +# weechat::print("", "\t".$string); + + $cb_datap = $_[0]; + $cb_bufferp = $_[1]; + $cb_date = $_[2]; + $cb_tags = $_[3]; + $cb_disp = $_[4]; + $cb_high = $_[5]; + $cb_prefix = $_[6]; + $cb_msg = $_[7]; + + # Only work on highlighted messages or private message when enabled + if ($cb_high == "1" || (weechat::config_get_plugin("merge_private") eq "on" && $cb_tags =~ /notify_private/)) + { + # Pre bug #29618 (0.3.3) away detect + if (weechat::info_get("version_number", "") <= 0x00030200) + { + $away = ''; + # Get infolist for this server + $infolist = weechat::infolist_get("irc_server", "", weechat::buffer_get_string($cb_bufferp, "localvar_server")); + while (weechat::infolist_next($infolist)) + { + # Get away message is is_away is on + $away = weechat::infolist_string($infolist, "away_message") if (weechat::infolist_integer($infolist, "is_away")); + } + weechat::infolist_free($infolist); + } + # Post bug #29618 fix + else + { + $away = weechat::buffer_get_string($cb_bufferp, "localvar_away"); + } + if (weechat::config_get_plugin("away_only") ne "on" || ($away ne "")) + { + # Check buffer name is an IRC channel + $bufname = weechat::buffer_get_string($cb_bufferp, 'name'); + if ($bufname =~ /(.*)\.([#&\+!])(.*)/) + { + # Are we running on this channel + if (weechat::config_get_plugin($bufname) ne "off" && $cb_disp eq "1") + { + # Format nick + # Line isn't action or topic notify + if (!($cb_tags =~ /irc_action/) && !($cb_tags =~ /irc_topic/)) + { + # Strip nick colour + $uncolnick = weechat::string_remove_color($cb_prefix, ""); + # Format nick + $nick = " ".weechat::config_get_plugin("nick_prefix").weechat::color("chat_highlight").$uncolnick.weechat::color("reset").weechat::config_get_plugin("nick_suffix"); + } + # Topic line + elsif ($cb_tags =~ /irc_topic/) + { + $nick = " ".$cb_prefix.weechat::color("reset"); + } + # Action line + else + { + $uncolnick = weechat::string_remove_color($cb_prefix, ""); + $nick = weechat::color("chat_highlight").$uncolnick.weechat::color("reset"); + } + # Send to output + highmon_print ($cb_msg, $cb_bufferp, $nick, $cb_date, $cb_tags); + } + } + # Or is private message + elsif (weechat::config_get_plugin("merge_private") eq "on" && $cb_tags =~ /notify_private/) + { + # Strip nick colour + $uncolnick = weechat::buffer_get_string($cb_bufferp, 'short_name'); + # Format nick + $nick = " ".weechat::config_get_plugin("nick_prefix").weechat::color("chat_highlight").$uncolnick.weechat::color("reset").weechat::config_get_plugin("nick_suffix"); + #Send to output + highmon_print ($cb_msg, $cb_bufferp, $nick, $cb_date, $cb_tags); + } + } + } + return weechat::WEECHAT_RC_OK; +} + +# Output formatter and printer takes (msg bufpointer nick) +sub highmon_print +{ + $cb_msg = $_[0]; + my $cb_bufferp = $_[1] if ($_[1]); + my $nick = $_[2] if ($_[2]); + my $cb_date = $_[3] if ($_[3]); + my $cb_tags = $_[4] if ($_[4]); + + #Normal channel message + if ($cb_bufferp && $nick) + { + # Format buffer name + $bufname = format_buffer_name($cb_bufferp); + + # If alignment is #channel | nick msg + if (weechat::config_get_plugin("alignment") eq "channel") + { + $nick =~ s/\s(.*)/$1/; + # Build string + $outstr = $bufname."\t".$nick." ".$cb_msg; + } + # or if it is channel number | nick msg + elsif (weechat::config_get_plugin("alignment") eq "schannel") + { + $nick =~ s/\s(.*)/$1/; + # Use channel number instead + $bufname = weechat::color("chat_prefix_buffer").weechat::buffer_get_integer($cb_bufferp, 'number').weechat::color("reset"); + # Build string + $outstr = $bufname."\t".$nick." ".$cb_msg; + } + # or if it is number:#channel | nick msg + elsif (weechat::config_get_plugin("alignment") eq "nchannel") + { + $nick =~ s/\s(.*)/$1/; + # Place channel number in front of formatted name + $bufname = weechat::color("chat_prefix_buffer").weechat::buffer_get_integer($cb_bufferp, 'number').":".weechat::color("reset").$bufname; + # Build string + $outstr = $bufname."\t".$nick." ".$cb_msg; + } + # or if it is #channel nick | msg + elsif (weechat::config_get_plugin("alignment") eq "channel,nick") + { + # Build string + $outstr = $bufname.":".$nick."\t".$cb_msg; + } + # or if it is channel number nick | msg + elsif (weechat::config_get_plugin("alignment") eq "schannel,nick") + { + # Use channel number instead + $bufname = weechat::color("chat_prefix_buffer").weechat::buffer_get_integer($cb_bufferp, 'number').weechat::color("reset"); + # Build string + $outstr = $bufname.":".$nick."\t".$cb_msg; + } + # or if it is number:#channel nick | msg + elsif (weechat::config_get_plugin("alignment") eq "nchannel,nick") + { + # Place channel number in front of formatted name + $bufname = weechat::color("chat_prefix_buffer").weechat::buffer_get_integer($cb_bufferp, 'number').":".weechat::color("reset").$bufname; + # Build string + $outstr = $bufname.":".$nick."\t".$cb_msg; + } + # or finally | #channel nick msg + else + { + # Build string + $outstr = "\t".$bufname.":".$nick." ".$cb_msg; + } + } + # highmon channel toggle message + elsif ($cb_bufferp && !$nick) + { + # Format buffer name + $bufname = format_buffer_name($cb_bufferp); + + # If alignment is #channel * | * + if (weechat::config_get_plugin("alignment") =~ /channel/) + { + # If it's actually channel number * | * + if (weechat::config_get_plugin("alignment") =~ /schannel/) + { + # Use channel number instead + $bufname = weechat::color("chat_prefix_buffer").weechat::buffer_get_integer($cb_bufferp, 'number').weechat::color("reset"); + } + # Or if it's actually number:#channel * | * + if (weechat::config_get_plugin("alignment") =~ /nchannel/) + { + # Place channel number in front of formatted name + $bufname = weechat::color("chat_prefix_buffer").weechat::buffer_get_integer($cb_bufferp, 'number').":".weechat::color("reset").$bufname; + } + $outstr = $bufname."\t".$cb_msg; + } + # or if alignment is | * + else + { + $outstr = $bufname.": ".$cb_msg; + } + } + # highmon dynmon + elsif (!$cb_bufferp && !$nick) + { + $outstr = "\t".$cb_msg; + } + + # Send string to buffer + if (weechat::config_get_plugin("output") eq "buffer") + { + # Search for and confirm buffer + $highmon_buffer = weechat::buffer_search("perl", "highmon"); + # Print + if ($cb_date) + { + weechat::print_date_tags($highmon_buffer, $cb_date, $cb_tags, $outstr); + } + else + { + weechat::print($highmon_buffer, $outstr); + } + } + elsif (weechat::config_get_plugin("output") eq "bar") + { + # Add time string + use POSIX qw(strftime); + if ($cb_date) + { + $time = strftime(weechat::config_string(weechat::config_get("weechat.look.buffer_time_format")), localtime($cb_date)); + } + else + { + $time = strftime(weechat::config_string(weechat::config_get("weechat.look.buffer_time_format")), localtime); + } + # Colourise + if ($time =~ /\$\{(?:color:)?[\w,]+\}/) # Coloured string + { + while ($time =~ /\$\{(?:color:)?([\w,]+)\}/) + { + $color = weechat::color($1); + $time =~ s/\$\{(?:color:)?[\w,]+\}/$color/; + } + $time .= weechat::color("reset"); + } + else # Default string + { + $colour = weechat::color(weechat::config_string(weechat::config_get("weechat.color.chat_time_delimiters"))); + $reset = weechat::color("reset"); + $time =~ s/(\d*)(.)(\d*)/$1$colour$2$reset$3/g; + } + # Push updates to bar lists + push (@bar_lines_time, $time); + + # Change tab char + $delim = " ".weechat::color(weechat::config_string(weechat::config_get("weechat.color.chat_delimiters"))).weechat::config_string(weechat::config_get("weechat.look.prefix_suffix")).weechat::color("reset")." "; + $outstr =~ s/\t/$delim/; + + push (@bar_lines, $outstr); + # Trigger update + weechat::bar_item_update("highmon"); + + if (weechat::config_get_plugin("bar_scrolldown") eq "on") + { + weechat::command("", "/bar scroll highmon * ye") + } + } +} + +# Start the output display +sub highmon_start +{ + if (weechat::config_get_plugin("output") eq "buffer") + { + highmon_buffer_open(); + } + elsif (weechat::config_get_plugin("output") eq "bar") + { + highmon_bar_open(); + } +} + +# Takes two optional args (channel server), toggles monitoring on/off +sub highmon_toggle +{ + $data = $_[0]; + $buffer = $_[1]; + $args = $_[2]; + + # Check if we've been told what channel to act on + if ($args ne "") + { + # Split argument up + @arg_array = split(/ /,$args); + # Check if a server was given + if ($arg_array[1]) + { + # Find matching + $bufp = weechat::buffer_search("irc", $arg_array[1].".".$arg_array[0]); + } + else + { + $found_chans = 0; + # Loop through defined servers + $infolist = weechat::infolist_get("buffer", "", ""); + while (weechat::infolist_next($infolist)) + { + # Only interesting in IRC buffers + if (weechat::infolist_string($infolist, "plugin_name") eq "irc") + { + # Find buffers that maych + $sname = weechat::infolist_string($infolist, "short_name"); + if ($sname eq $arg_array[0]) + { + $found_chans++; + $bufp = weechat::infolist_pointer($infolist, "pointer"); + } + } + } + weechat::infolist_free($infolist); + # If the infolist found more than one channel, halt as we need to know which one + if ($found_chans > 1) + { + weechat::print("", "Channel name is not unique, please define server"); + return weechat::WEECHAT_RC_OK; + } + } + # Something didn't return right + if ($bufp eq "") + { + weechat::print("", "Could not find buffer"); + return weechat::WEECHAT_RC_OK; + } + } + else + { + # Get pointer from where we are + $bufp = weechat::current_buffer(); + } + # Get buffer name + $bufname = weechat::buffer_get_string($bufp, 'name'); + # Test if buffer is an IRC channel + if ($bufname =~ /(.*)\.([#&\+!])(.*)/) + { + if (weechat::config_get_plugin($bufname) eq "off") + { + # If currently off, set on + weechat::config_set_plugin($bufname, "on"); + + # Send to output formatter + highmon_print("Highlight Monitoring Enabled", $bufp); + return weechat::WEECHAT_RC_OK; + } + elsif (weechat::config_get_plugin($bufname) eq "on" || weechat::config_get_plugin($bufname) eq "") + { + # If currently on, set off + weechat::config_set_plugin($bufname, "off"); + + # Send to output formatter + highmon_print("Highlight Monitoring Disabled", $bufp); + return weechat::WEECHAT_RC_OK; + } + } +} + +# Takes a buffer pointer and returns a formatted name +sub format_buffer_name +{ + $cb_bufferp = $_[0]; + $bufname = weechat::buffer_get_string($cb_bufferp, 'name'); + + # Set colour from buffer name + if (weechat::config_get_plugin("color_buf") eq "on") + { + # Determine what colour to use + $color = weechat::info_get("irc_nick_color", $bufname); + if (!$color) + { + $color = 0; + @char_array = split(//,$bufname); + foreach $char (@char_array) + { + $color += ord($char); + } + $color %= 10; + $color = sprintf "weechat.color.chat_nick_color%02d", $color+1; + $color = weechat::config_get($color); + $color = weechat::config_string($color); + $color = weechat::color($color); + } + + # Private message just show network + if (weechat::config_get_plugin("merge_private") eq "on" && weechat::buffer_get_string($cb_bufferp, "localvar_type") eq "private") + { + $bufname = weechat::buffer_get_string($cb_bufferp, "localvar_server"); + } + # Format name to short or 'nicename' + elsif (weechat::config_get_plugin("short_names") eq "on") + { + $bufname = weechat::buffer_get_string($cb_bufferp, 'short_name'); + } + else + { + $bufname =~ s/(.*)\.([#&\+!])(.*)/$1$2$3/; + } + + # Build a coloured string + $bufname = $color.$bufname.weechat::color("reset"); + } + # User set colour name + elsif (weechat::config_get_plugin("color_buf") ne "off") + { + # Private message just show network + if (weechat::config_get_plugin("merge_private") eq "on" && weechat::buffer_get_string($cb_bufferp, "localvar_type") eq "private") + { + $bufname = weechat::buffer_get_string($cb_bufferp, "localvar_server"); + } + # Format name to short or 'nicename' + elsif (weechat::config_get_plugin("short_names") eq "on") + { + $bufname = weechat::buffer_get_string($cb_bufferp, 'short_name'); + } + else + { + $bufname =~ s/(.*)\.([#&\+!])(.*)/$1$2$3/; + } + + $color = weechat::config_get_plugin("color_buf"); + $bufname = weechat::color($color).$bufname.weechat::color("reset"); + } + # Stick with default colour + else + { + # Private message just show network + if (weechat::config_get_plugin("merge_private") eq "on" && weechat::buffer_get_string($cb_bufferp, "localvar_type") eq "private") + { + $bufname = weechat::buffer_get_string($cb_bufferp, "localvar_server"); + } + # Format name to short or 'nicename' + elsif (weechat::config_get_plugin("short_names") eq "on") + { + $bufname = weechat::buffer_get_string($cb_bufferp, 'short_name'); + } + else + { + $bufname =~ s/(.*)\.([#&\+!])(.*)/$1$2$3/; + } + } + + return $bufname; +} + +# Check result of register, and attempt to behave in a sane manner +if (!weechat::register("highmon", "KenjiE20", "2.7", "GPL3", "Highlight Monitor", "", "")) +{ + # Double load + weechat::print ("", "\tHighmon is already loaded"); + return weechat::WEECHAT_RC_OK; +} +else +{ + # Start everything + highmon_hook(); + highmon_config_init(); + highmon_start(); +} diff --git a/config/weechat/perl/multiline.pl b/config/weechat/perl/multiline.pl new file mode 100644 index 00000000..54474d41 --- /dev/null +++ b/config/weechat/perl/multiline.pl @@ -0,0 +1,782 @@ +use strict; use warnings; +$INC{'Encode/ConfigLocal.pm'}=1; +require Encode; +use utf8; + +# multiline.pl is written by Nei +# and licensed under the under GNU General Public License v3 +# or any later version + +# to read the following docs, you can use "perldoc multiline.pl" + +=head1 NAME + +multiline - Multi-line edit box for WeeChat (weechat edition) + +=head1 DESCRIPTION + +multiline will draw a multi-line edit box to your WeeChat window so +that when you hit the return key, you can first compose a complete +multi-line message before sending it all at once. + +Furthermore, if you have multi-line pastes then you can edit them +before sending out all the lines. + +=head1 USAGE + +make a key binding to send the finished message: + + /key bind meta-s /input return + +then you can send the multi-line message with Alt+S + +=head1 SETTINGS + +the settings are usually found in the + + plugins.var.perl.multiline + +namespace, that is, type + + /set plugins.var.perl.multiline.* + +to see them and + + /set plugins.var.perl.multiline.SETTINGNAME VALUE + +to change a setting C to a new value C. Finally, + + /unset plugins.var.perl.multiline.SETTINGNAME + +will reset a setting to its default value. + +the following settings are available: + +=head2 char + +character(s) which should be displayed to indicate end of line + +=head2 tab + +character(s) which should be displayed instead of Tab key character + +=head2 lead_linebreak + +if turned on, multi-line messages always start on a new line + +=head2 modify_keys + +if turned on, cursor keys are modified so that they respect line +boundaries instead of treating the whole multi-line message as a +single line + +=head2 magic + +indicator displayed when message will be sent soon + +=head2 magic_enter_time + +delay after pressing enter before sending automatically (in ms), or 0 +to disable + +=head2 magic_paste_only + +only use multi-line messages for multi-line pastes (multi-line on +enter is disabled by this) + +=head2 paste_lock + +time-out to detect pastes (disable the weechat built-in paste +detection if you want to use this) + +=head2 send_empty + +set to on to automatically disregard enter key on empty line + +=head2 hide_magic_nl + +whether the new line inserted by magic enter key will be hidden + +=head2 weechat_paste_fix + +disable ctrl-J binding when paste is detected to stop silly weechat +sending out pastes without allowing to edit them + +=head2 ipl + +this setting controls override of ctrl-M (enter key) by script. Turn +it off if you don't want multiline.pl to set and re-set the key binding. + +=head1 FUNCTION DESCRIPTION + +for full pod documentation, filter this script with + + perl -pE' + (s/^## (.*?) -- (.*)/=head2 $1\n\n$2\n\n=over\n/ and $o=1) or + s/^## (.*?) - (.*)/=item I<$1>\n\n$2\n/ or + (s/^## (.*)/=back\n\n$1\n\n=cut\n/ and $o=0,1) or + ($o and $o=0,1 and s/^sub /=back\n\n=cut\n\nsub /)' + +=cut + +use constant SCRIPT_NAME => 'multiline'; +our $VERSION = '0.6.3'; # af2e0a17b659a16 +weechat::register(SCRIPT_NAME, + 'Nei ', # Author + $VERSION, + 'GPL3', # License + 'Multi-line edit box', # Description + 'stop_multiline', '') || return; +sub SCRIPT_FILE() { + my $infolistptr = weechat::infolist_get('perl_script', '', SCRIPT_NAME); + my $filename = weechat::infolist_string($infolistptr, 'filename') if weechat::infolist_next($infolistptr); + weechat::infolist_free($infolistptr); + return $filename unless @_; +} + +{ +package Nlib; +# this is a weechat perl library +use strict; use warnings; no warnings 'redefine'; + +## i2h -- copy weechat infolist content into perl hash +## $infolist - name of the infolist in weechat +## $ptr - pointer argument (infolist dependend) +## @args - arguments to the infolist (list dependend) +## $fields - string of ref type "fields" if only certain keys are needed (optional) +## returns perl list with perl hashes for each infolist entry +sub i2h { + my %i2htm = (i => 'integer', s => 'string', p => 'pointer', b => 'buffer', t => 'time'); + local *weechat::infolist_buffer = sub { '(not implemented)' }; + my ($infolist, $ptr, @args) = @_; + $ptr ||= ""; + my $fields = ref $args[-1] eq 'fields' ? ${ pop @args } : undef; + my $infptr = weechat::infolist_get($infolist, $ptr, do { local $" = ','; "@args" }); + my @infolist; + while (weechat::infolist_next($infptr)) { + my @fields = map { + my ($t, $v) = split ':', $_, 2; + bless \$v, $i2htm{$t}; + } + split ',', + ($fields || weechat::infolist_fields($infptr)); + push @infolist, +{ do { + my (%list, %local, @local); + map { + my $fn = 'weechat::infolist_'.ref $_; + my $r = do { no strict 'refs'; &$fn($infptr, $$_) }; + if ($$_ =~ /^localvar_name_(\d+)$/) { + $local[$1] = $r; + () + } + elsif ($$_ =~ /^(localvar)_value_(\d+)$/) { + $local{$local[$2]} = $r; + $1 => \%local + } + elsif ($$_ =~ /(.*?)((?:_\d+)+)$/) { + my ($key, $idx) = ($1, $2); + my @idx = split '_', $idx; shift @idx; + my $target = \$list{$key}; + for my $x (@idx) { + my $o = 1; + if ($key eq 'key' or $key eq 'key_command') { + $o = 0; + } + if ($x-$o < 0) { + local $" = '|'; + weechat::print('',"list error: $target/$$_/$key/$x/$idx/@idx(@_)"); + $o = 0; + } + $target = \$$target->[$x-$o] + } + $$target = $r; + + $key => $list{$key} + } + else { + $$_ => $r + } + } @fields + } }; + } + weechat::infolist_free($infptr); + !wantarray && @infolist ? \@infolist : @infolist +} + +## hdh -- hdata helper +## $_[0] - arg pointer or hdata list name +## $_[1] - hdata name +## $_[2..$#_] - hdata variable name +## $_[-1] - hashref with key/value to update (optional) +## returns value of hdata, and hdata name in list ctx, or number of variables updated +sub hdh { + if (@_ > 1 && $_[0] !~ /^0x/ && $_[0] !~ /^\d+$/) { + my $arg = shift; + unshift @_, weechat::hdata_get_list(weechat::hdata_get($_[0]), $arg); + } + while (@_ > 2) { + my ($arg, $name, $var) = splice @_, 0, 3; + my $hdata = weechat::hdata_get($name); + unless (ref $var eq 'HASH') { + $var =~ s/!(.*)/weechat::hdata_get_string($hdata, $1)/e; + (my $plain_var = $var) =~ s/^\d+\|//; + my $type = weechat::hdata_get_var_type_string($hdata, $plain_var); + if ($type eq 'pointer') { + my $name = weechat::hdata_get_var_hdata($hdata, $var); + unshift @_, $name if $name; + } + + my $fn = "weechat::hdata_$type"; + unshift @_, do { no strict 'refs'; + &$fn($hdata, $arg, $var) }; + } + else { + return weechat::hdata_update($hdata, $arg, $var); + } + } + wantarray ? @_ : $_[0] +} + +use Pod::Select qw(); +use Pod::Simple::TextContent; + +## get_desc_from_pod -- return setting description from pod documentation +## $file - filename with pod +## $setting - name of setting +## returns description as text +sub get_desc_from_pod { + my $file = shift; + return unless -s $file; + my $setting = shift; + + open my $pod_sel, '>', \my $ss; + Pod::Select::podselect({ + -output => $pod_sel, + -sections => ["SETTINGS/$setting"]}, $file); + + my $pt = new Pod::Simple::TextContent; + $pt->output_string(\my $ss_f); + $pt->parse_string_document($ss); + + my ($res) = $ss_f =~ /^\s*\Q$setting\E\s+(.*)\s*/; + $res +} + +## get_settings_from_pod -- retrieve all settings in settings section of pod +## $file - file with pod +## returns list of all settings +sub get_settings_from_pod { + my $file = shift; + return unless -s $file; + + open my $pod_sel, '>', \my $ss; + Pod::Select::podselect({ + -output => $pod_sel, + -sections => ["SETTINGS//!.+"]}, $file); + + $ss =~ /^=head2\s+(.*)\s*$/mg +} + +## mangle_man_for_wee -- turn man output into weechat codes +## @_ - list of grotty lines that should be turned into weechat attributes +## returns modified lines and modifies lines in-place +sub mangle_man_for_wee { + for (@_) { + s/_\x08(.)/weechat::color('underline').$1.weechat::color('-underline')/ge; + s/(.)\x08\1/weechat::color('bold').$1.weechat::color('-bold')/ge; + } + wantarray ? @_ : $_[0] +} + +## read_manpage -- read a man page in weechat window +## $file - file with pod +## $name - buffer name +sub read_manpage { + my $caller_package = (caller)[0]; + my $file = shift; + my $name = shift; + + if (my $obuf = weechat::buffer_search('perl', "man $name")) { + eval qq{ + package $caller_package; + weechat::buffer_close(\$obuf); + }; + } + + my @wee_keys = Nlib::i2h('key'); + my @keys; + + my $winptr = weechat::current_window(); + my ($wininfo) = Nlib::i2h('window', $winptr); + my $buf = weechat::buffer_new("man $name", '', '', '', ''); + return weechat::WEECHAT_RC_OK unless $buf; + + my $width = $wininfo->{chat_width}; + --$width if $wininfo->{chat_width} < $wininfo->{width} || ($wininfo->{width_pct} < 100 && (grep { $_->{y} == $wininfo->{y} } Nlib::i2h('window'))[-1]{x} > $wininfo->{x}); + $width -= 2; # when prefix is shown + + weechat::buffer_set($buf, 'time_for_each_line', 0); + eval qq{ + package $caller_package; + weechat::buffer_set(\$buf, 'display', 'auto'); + }; + die $@ if $@; + + @keys = map { $_->{key} } + grep { $_->{command} eq '/input history_previous' || + $_->{command} eq '/input history_global_previous' } @wee_keys; + @keys = 'meta2-A' unless @keys; + weechat::buffer_set($buf, "key_bind_$_", '/window scroll -1') for @keys; + + @keys = map { $_->{key} } + grep { $_->{command} eq '/input history_next' || + $_->{command} eq '/input history_global_next' } @wee_keys; + @keys = 'meta2-B' unless @keys; + weechat::buffer_set($buf, "key_bind_$_", '/window scroll +1') for @keys; + + weechat::buffer_set($buf, 'key_bind_ ', '/window page_down'); + + @keys = map { $_->{key} } + grep { $_->{command} eq '/input delete_previous_char' } @wee_keys; + @keys = ('ctrl-?', 'ctrl-H') unless @keys; + weechat::buffer_set($buf, "key_bind_$_", '/window page_up') for @keys; + + weechat::buffer_set($buf, 'key_bind_g', '/window scroll_top'); + weechat::buffer_set($buf, 'key_bind_G', '/window scroll_bottom'); + + weechat::buffer_set($buf, 'key_bind_q', '/buffer close'); + + weechat::print($buf, " \t".mangle_man_for_wee($_)) # weird bug with \t\t showing nothing? + for `pod2man \Q$file\E 2>/dev/null | GROFF_NO_SGR=1 nroff -mandoc -rLL=${width}n -rLT=${width}n -Tutf8 2>/dev/null`; + weechat::command($buf, '/window scroll_top'); + + unless (hdh($buf, 'buffer', 'lines', 'lines_count') > 0) { + weechat::print($buf, weechat::prefix('error').$_) + for "Unfortunately, your @{[weechat::color('underline')]}nroff". + "@{[weechat::color('-underline')]} command did not produce". + " any output.", + "Working pod2man and nroff commands are required for the ". + "help viewer to work.", + "In the meantime, please use the command ", '', + "\tperldoc $file", '', + "on your shell instead in order to read the manual.", + "Thank you and sorry for the inconvenience." + } +} + +1 +} + +our $MAGIC_ENTER_TIMER; +our $MAGIC_LOCK; +our $MAGIC_LOCK_TIMER; +our $WEECHAT_PASTE_FIX_CTRLJ_CMD; +our $INPUT_CHANGED_EATER_FLAG; +our $IGNORE_INPUT_CHANGED; +our $IGNORE_INPUT_CHANGED2; + +use constant KEY_RET => 'ctrl-M'; +use constant INPUT_NL => '/input insert \x0a'; +use constant INPUT_MAGIC => '/input magic_enter'; +our $NL = "\x0a"; + +init_multiline(); + +my $magic_enter_cancel_dynamic = 1; +my $paste_undo_start_ignore_dynamic = 0; +my $input_changed_eater_dynamic = 0; +my $multiline_complete_fix_dynamic = 1; + +sub magic_enter_cancel_dynamic { $magic_enter_cancel_dynamic ? &magic_enter_cancel : weechat::WEECHAT_RC_OK } +sub paste_undo_start_ignore_dynamic { $paste_undo_start_ignore_dynamic ? &paste_undo_start_ignore : weechat::WEECHAT_RC_OK } +sub input_changed_eater_dynamic { $input_changed_eater_dynamic ? &input_changed_eater : weechat::WEECHAT_RC_OK } +sub multiline_complete_fix_dynamic { $multiline_complete_fix_dynamic ? &multiline_complete_fix : weechat::WEECHAT_RC_OK } + +weechat::hook_config('plugins.var.perl.'.SCRIPT_NAME.'.*', 'default_options', ''); +weechat::hook_modifier('input_text_display_with_cursor', 'multiline_display', ''); +weechat::hook_command_run('/help '.SCRIPT_NAME, 'help_cmd', ''); +weechat::hook_command_run(INPUT_MAGIC, 'magic_enter', ''); +weechat::hook_signal('input_text_*', 'magic_enter_cancel_dynamic', ''); +weechat::hook_command_run('/input *', 'paste_undo_start_ignore_dynamic', ''); +weechat::hook_signal('2000|input_text_changed', 'input_changed_eater_dynamic', ''); +weechat::hook_signal('key_pressed', 'magic_lock_hatch', ''); +# we need lower than default priority here or the first character is separated +weechat::hook_signal('500|input_text_changed', 'paste_undo_hack', '') + # can only do this on weechat 0.4.0 + if (weechat::info_get('version_number', '') || 0) >= 0x00040000; +weechat::hook_command_run("1500|/input complete*", 'multiline_complete_fix_dynamic', 'complete*'); +weechat::hook_command_run("1500|/input delete_*", 'multiline_complete_fix_dynamic', 'delete_*'); +weechat::hook_command_run("1500|/input move_*", 'multiline_complete_fix_dynamic', 'move_*'); + +sub _stack_depth { + my $depth = -1; + 1 while caller(++$depth); + $depth; +} + +## multiline_display -- show multi-lines on display of input string +## () - modifier handler +## $_[2] - buffer pointer +## $_[3] - input string +## returns modified input string +sub multiline_display { + Encode::_utf8_on($_[3]); + Encode::_utf8_on(my $nl = weechat::config_get_plugin('char') || ' '); + Encode::_utf8_on(my $tab = weechat::config_get_plugin('tab')); + my $cb = weechat::current_buffer() eq $_[2] && $MAGIC_ENTER_TIMER; + if ($cb) { + $_[3] =~ s/$NL\x19b#/\x19b#/ if weechat::config_string_to_boolean(weechat::config_get_plugin('hide_magic_nl')); + } + if ($_[3] =~ s/$NL/$nl\x0d/g) { + $_[3] =~ s/\A/ \x0d/ if weechat::config_string_to_boolean(weechat::config_get_plugin('lead_linebreak')); + } + $_[3] =~ s/\x09/$tab/g if $tab; + if ($cb) { + Encode::_utf8_on(my $magic = weechat::config_get_plugin('magic')); + $_[3] =~ s/\Z/$magic/ if $magic; + } + $_[3] +} + +## lock_timer_exp -- expire the magic lock timer +sub lock_timer_exp { + if ($MAGIC_LOCK_TIMER) { + weechat::unhook($MAGIC_LOCK_TIMER); + $MAGIC_LOCK_TIMER = undef; + } + weechat::WEECHAT_RC_OK +} + +## paste_undo_stop_ignore -- unset ignore2 flag +sub paste_undo_stop_ignore { + $IGNORE_INPUT_CHANGED2 = undef; + weechat::WEECHAT_RC_OK +} + +## paste_undo_start_ignore -- set ignore2 flag when /input is received so to allow /input undo/redo +## () - command_run handler +## $_[2] - command that was called +sub paste_undo_start_ignore { + return weechat::WEECHAT_RC_OK if $IGNORE_INPUT_CHANGED; + return weechat::WEECHAT_RC_OK if $_[2] =~ /insert/; + $IGNORE_INPUT_CHANGED2 = 1; + weechat::WEECHAT_RC_OK +} + +## paste_undo_hack -- fix up undo stack when paste is detected by calling /input undo +## () - signal handler +## $_[2] - buffer pointer +sub paste_undo_hack { + return weechat::WEECHAT_RC_OK if $IGNORE_INPUT_CHANGED; + return paste_undo_stop_ignore() if $IGNORE_INPUT_CHANGED2; + if ($MAGIC_LOCK > 0 && get_lock_enabled()) { + signall_ignore_input_changed(1); + $paste_undo_start_ignore_dynamic = 1; + + Encode::_utf8_on(my $input = weechat::buffer_get_string($_[2], 'input')); + my $pos = weechat::buffer_get_integer($_[2], 'input_pos'); + + weechat::command($_[2], '/input undo') for 1..2; + + weechat::buffer_set($_[2], 'input', $input); + weechat::buffer_set($_[2], 'input_pos', $pos); + + $paste_undo_start_ignore_dynamic = 0; + signall_ignore_input_changed(0); + } + weechat::WEECHAT_RC_OK +} + +## input_changed_eater -- suppress input_text_changed signal on new weechats +## () - signal handler +sub input_changed_eater { + $INPUT_CHANGED_EATER_FLAG = undef; + weechat::WEECHAT_RC_OK_EAT +} + +## signall_ignore_input_changed -- use various methods to "ignore" input_text_changed signal +## $_[0] - start ignore or stop ignore +sub signall_ignore_input_changed { + if ($_[0]) { + weechat::hook_signal_send('input_flow_free', weechat::WEECHAT_HOOK_SIGNAL_INT, 1); + $input_changed_eater_dynamic = 1; + $IGNORE_INPUT_CHANGED = 1; + weechat::buffer_set('', 'completion_freeze', '1'); + } + else { + weechat::buffer_set('', 'completion_freeze', '0'); + $IGNORE_INPUT_CHANGED = undef; + $input_changed_eater_dynamic = 0; + weechat::hook_signal_send('input_flow_free', weechat::WEECHAT_HOOK_SIGNAL_INT, 0); + } +} + +## multiline_complete_fix -- add per line /input handling for completion, movement and deletion +## () - command_run handler +## $_[0] - original bound data +## $_[1] - buffer pointer +## $_[2] - original command +sub multiline_complete_fix { + $magic_enter_cancel_dynamic = 0; + $multiline_complete_fix_dynamic = 0; + if ($_[2] =~ s/_message$/_line/ || !weechat::config_string_to_boolean(weechat::config_get_plugin('modify_keys'))) { + weechat::command($_[1], $_[2]); + } + else { + signall_ignore_input_changed(1); + Encode::_utf8_on(my $input = weechat::buffer_get_string($_[1], 'input')); + my $pos = weechat::buffer_get_integer($_[1], 'input_pos'); + if ($pos && $_[2] =~ /(?:previous|beginning_of)_/ && (substr $input, $pos-1, 1) eq $NL) { + substr $input, $pos-1, 1, "\0" + } + elsif ($pos < length $input && $_[2] =~ /(?:next|end_of)_/ && (substr $input, $pos, 1) eq $NL) { + substr $input, $pos, 1, "\0" + } + my @lines = $pos ? (split /$NL/, (substr $input, 0, $pos), -1) : ''; + my @after = $pos < length $input ? (split /$NL/, (substr $input, $pos), -1) : ''; + $lines[-1] =~ s/\0$/$NL/; + $after[0] =~ s/^\0/$NL/; + my ($p1, $p2) = (pop @lines, shift @after); + weechat::buffer_set($_[1], 'input', $p1.$p2); + weechat::buffer_set($_[1], 'input_pos', length $p1); + + $magic_enter_cancel_dynamic = 1; + $INPUT_CHANGED_EATER_FLAG = 1; + weechat::command($_[1], $_[2]); + my $changed_later = !$INPUT_CHANGED_EATER_FLAG; + magic_enter_cancel() if $changed_later; + $magic_enter_cancel_dynamic = 0; + + Encode::_utf8_on(my $p = weechat::buffer_get_string($_[1], 'input')); + $pos = weechat::buffer_get_integer($_[1], 'input_pos'); + weechat::command($_[1], '/input undo') if @lines || @after; + weechat::command($_[1], '/input undo'); + weechat::buffer_set($_[1], 'input', join $NL, @lines, $p, @after); + weechat::buffer_set($_[1], 'input_pos', $pos+length join $NL, @lines, ''); + + signall_ignore_input_changed(0); + weechat::hook_signal_send('input_text_changed', weechat::WEECHAT_HOOK_SIGNAL_POINTER, $_[1]) if $changed_later; + } + $multiline_complete_fix_dynamic = 1; + $magic_enter_cancel_dynamic = 1; + weechat::WEECHAT_RC_OK_EAT +} + +## help_cmd -- show multi-line script documentation +## () - command_run handler +sub help_cmd { + Nlib::read_manpage(SCRIPT_FILE, SCRIPT_NAME); + weechat::WEECHAT_RC_OK_EAT +} + +## get_lock_time -- gets timeout for paste detection according to setting +## returns timeout (at least 1) +sub get_lock_time { + my $lock_time = weechat::config_get_plugin('paste_lock'); + $lock_time = 1 unless $lock_time =~ /^\d+$/ && $lock_time; + $lock_time +} + +## get_lock_enabled -- checks whether the paste detection lock is enabled +## returns bool +sub get_lock_enabled { + my $lock = weechat::config_get_plugin('paste_lock'); + $lock = weechat::config_string_to_boolean($lock) + unless $lock =~ /^\d+$/; + $lock +} + +## magic_lock_hatch -- set a timer for paste detection +## () - signal handler +sub magic_lock_hatch { + lock_timer_exp(); + $MAGIC_LOCK_TIMER = weechat::hook_timer(get_lock_time(), 0, 1, 'lock_timer_exp', ''); + weechat::WEECHAT_RC_OK +} + +## magic_unlock -- reduce the lock added by paste detection +## () - timer handler +sub magic_unlock { + if ($MAGIC_LOCK_TIMER) { + weechat::hook_timer(get_lock_time(), 0, 1, 'magic_unlock', ''); + } + else { + --$MAGIC_LOCK; + if (!$MAGIC_LOCK && $WEECHAT_PASTE_FIX_CTRLJ_CMD) { + do_key_bind('ctrl-J', $WEECHAT_PASTE_FIX_CTRLJ_CMD); + $WEECHAT_PASTE_FIX_CTRLJ_CMD = undef; + } + } + weechat::WEECHAT_RC_OK +} + +## get_magic_enter_time -- get timeout for auto-sending messages according to config +## returns timeout +sub get_magic_enter_time { + my $magic_enter = weechat::config_get_plugin('magic_enter_time'); + $magic_enter = 1000 * weechat::config_string_to_boolean($magic_enter) + unless $magic_enter =~ /^\d+$/; + $magic_enter +} + +## magic_enter -- receive enter key and do magic things: set up a timer for sending the message, add newline +## () - command_run handler +## $_[1] - buffer pointer +sub magic_enter { + Encode::_utf8_on(my $input = weechat::buffer_get_string($_[1], 'input')); + if (!length $input && weechat::config_string_to_boolean(weechat::config_get_plugin('send_empty'))) { + weechat::command($_[1], '/input return'); + } + else { + magic_enter_cancel(); + weechat::command($_[1], INPUT_NL); + + unless (get_lock_enabled() && $MAGIC_LOCK) { + if (weechat::config_string_to_boolean(weechat::config_get_plugin('magic_paste_only')) && + $input !~ /$NL/) { + magic_enter_send($_[1]); + } + elsif (my $magic_enter = get_magic_enter_time()) { + $MAGIC_ENTER_TIMER = weechat::hook_timer($magic_enter, 0, 1, 'magic_enter_send', $_[1]); + } + } + } + weechat::WEECHAT_RC_OK_EAT +} + +## magic_enter_send -- actually send enter key when triggered by magic_enter, remove preceding newline +## $_[0] - buffer pointer +## sending is delayed by 1ms to circumvent crash bug in api +sub magic_enter_send { + magic_enter_cancel(); + weechat::command($_[0], '/input delete_previous_char'); + weechat::command($_[0], '/wait 1ms /input return'); + weechat::WEECHAT_RC_OK +} + +## magic_enter_cancel -- cancel the timer for automatic sending of message, for example when more text was added, increase the paste lock for paste detection when used as signal handler +## () - signal handler when @_ is set +sub magic_enter_cancel { + if ($MAGIC_ENTER_TIMER) { + weechat::unhook($MAGIC_ENTER_TIMER); + $MAGIC_ENTER_TIMER = undef; + } + if ($MAGIC_LOCK_TIMER && @_) { + if (!$MAGIC_LOCK && !$WEECHAT_PASTE_FIX_CTRLJ_CMD && + weechat::config_string_to_boolean(weechat::config_get_plugin('weechat_paste_fix'))) { + ($WEECHAT_PASTE_FIX_CTRLJ_CMD) = get_key_command('ctrl-J'); + $WEECHAT_PASTE_FIX_CTRLJ_CMD = '-' unless defined $WEECHAT_PASTE_FIX_CTRLJ_CMD; + do_key_bind('ctrl-J', '-'); + } + if ($MAGIC_LOCK < 1) { + my $lock_time = get_lock_time(); + ++$MAGIC_LOCK; + weechat::hook_timer(get_lock_time(), 0, 1, 'magic_unlock', ''); + } + } + weechat::WEECHAT_RC_OK +} + +## need_magic_enter -- check if magic enter keybinding is needed according to config settings +## returns bool +sub need_magic_enter { + weechat::config_string_to_boolean(weechat::config_get_plugin('send_empty')) || get_magic_enter_time() || + weechat::config_string_to_boolean(weechat::config_get_plugin('magic_paste_only')) +} + +## do_key_bind -- mute execute a key binding, or unbind if $_[-1] is '-' +## @_ - arguments to /key bind +sub do_key_bind { + if ($_[-1] eq '-') { + pop; + weechat::command('', "/mute /key unbind @_"); + } + elsif ($_[-1] eq '!') { + pop; + weechat::command('', "/mute /key reset @_"); + } + else { + weechat::command('', "/mute /key bind @_"); + } +} + +{ my %keys; +## get_key_command -- get the command bound to a key +## $_[0] - key in weechat syntax +## returns the command + sub get_key_command { + unless (exists $keys{$_[0]}) { + ($keys{$_[0]}) = + map { $_->{command} } grep { $_->{key} eq $_[0] } + Nlib::i2h('key') + } + $keys{$_[0]} + } +} + +## default_options -- set up default option values on start and when unset +## () - config handler if @_ is set +sub default_options { + my %defaults = ( + char => '↩', + tab => '──▶▏', + magic => '‼', + ipl => 'on', + lead_linebreak => 'on', + modify_keys => 'on', + send_empty => 'on', + magic_enter_time => '1000', + paste_lock => '1', + magic_paste_only => 'off', + hide_magic_nl => 'on', + weechat_paste_fix => 'on', + ); + unless (weechat::config_is_set_plugin('ipl')) { + if (my $bar = weechat::bar_search('input')) { + weechat::bar_set($bar, $_, '0') for 'size', 'size_max'; + } + } + for (keys %defaults) { + weechat::config_set_plugin($_, $defaults{$_}) + unless weechat::config_is_set_plugin($_); + } + do_key_bind(KEY_RET, INPUT_NL) + if weechat::config_string_to_boolean(weechat::config_get_plugin('ipl')); + my ($enter_key) = get_key_command(KEY_RET); + if (need_magic_enter()) { + do_key_bind(KEY_RET, INPUT_MAGIC) + if $enter_key eq INPUT_NL; + } + else { + do_key_bind(KEY_RET, INPUT_NL) + if $enter_key eq INPUT_MAGIC; + } + weechat::WEECHAT_RC_OK +} + +sub init_multiline { + $MAGIC_LOCK = -1; + default_options(); + my $sf = SCRIPT_FILE; + for (Nlib::get_settings_from_pod($sf)) { + weechat::config_set_desc_plugin($_, Nlib::get_desc_from_pod($sf, $_)); + } + weechat::WEECHAT_RC_OK +} + +sub stop_multiline { + magic_enter_cancel(); + if (need_magic_enter()) { + my ($enter_key) = get_key_command(KEY_RET); + do_key_bind(KEY_RET, INPUT_NL) + if $enter_key eq INPUT_MAGIC; + } + if ($WEECHAT_PASTE_FIX_CTRLJ_CMD) { + do_key_bind('ctrl-J', $WEECHAT_PASTE_FIX_CTRLJ_CMD); + $WEECHAT_PASTE_FIX_CTRLJ_CMD = undef; + } + if (weechat::config_string_to_boolean(weechat::config_get_plugin('ipl'))) { + do_key_bind(KEY_RET, '!'); + } + weechat::WEECHAT_RC_OK +} diff --git a/config/weechat/plugins.conf b/config/weechat/plugins.conf new file mode 100644 index 00000000..a0a11cd0 --- /dev/null +++ b/config/weechat/plugins.conf @@ -0,0 +1,79 @@ +# +# weechat -- plugins.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[var] +perl.atcomplete.enabled = "on" +perl.highmon.alignment = "channel" +perl.highmon.away_only = "off" +perl.highmon.color_buf = "on" +perl.highmon.first_run = "true" +perl.highmon.hotlist_show = "off" +perl.highmon.logging = "off" +perl.highmon.merge_private = "off" +perl.highmon.nick_prefix = "<" +perl.highmon.nick_suffix = ">" +perl.highmon.output = "buffer" +perl.highmon.short_names = "off" +perl.multiline.char = "↩" +perl.multiline.hide_magic_nl = "on" +perl.multiline.ipl = "on" +perl.multiline.lead_linebreak = "on" +perl.multiline.magic = "‼" +perl.multiline.magic_enter_time = "1000" +perl.multiline.magic_paste_only = "off" +perl.multiline.modify_keys = "on" +perl.multiline.paste_lock = "1" +perl.multiline.send_empty = "on" +perl.multiline.tab = "──▶▏" +perl.multiline.weechat_paste_fix = "on" +python.chanotify.filters = "*:*" +python.chanotify.status = "on" +python.go.auto_jump = "off" +python.go.buffer_number = "on" +python.go.color_name = "black,cyan" +python.go.color_name_highlight = "red,cyan" +python.go.color_name_highlight_selected = "red,brown" +python.go.color_name_selected = "black,brown" +python.go.color_number = "yellow,magenta" +python.go.color_number_selected = "yellow,red" +python.go.fuzzy_search = "off" +python.go.message = "Go to: " +python.go.short_name = "off" +python.go.sort = "number,beginning" +python.go.use_core_instead_weechat = "off" + +[desc] +perl.atcomplete.enabled = "enable completion of nicks starting with @ (default: "on")" +perl.multiline.char = "character(s) which should be displayed to indicate end of line" +perl.multiline.hide_magic_nl = "whether the new line inserted by magic enter key will be hidden" +perl.multiline.ipl = "this setting controls override of ctrl-M (enter key) by script. Turn it off if you don't want multiline.pl to set and re-set the key binding." +perl.multiline.lead_linebreak = "if turned on, multi-line messages always start on a new line" +perl.multiline.magic = "indicator displayed when message will be sent soon" +perl.multiline.magic_enter_time = "delay after pressing enter before sending automatically (in ms), or 0 to disable" +perl.multiline.magic_paste_only = "only use multi-line messages for multi-line pastes (multi-line on enter is disabled by this)" +perl.multiline.modify_keys = "if turned on, cursor keys are modified so that they respect line boundaries instead of treating the whole multi-line message as a single line" +perl.multiline.paste_lock = "time-out to detect pastes (disable the weechat built-in paste detection if you want to use this)" +perl.multiline.send_empty = "set to on to automatically disregard enter key on empty line" +perl.multiline.tab = "character(s) which should be displayed instead of Tab key character" +perl.multiline.weechat_paste_fix = "disable ctrl-J binding when paste is detected to stop silly weechat sending out pastes without allowing to edit them" +python.go.auto_jump = "automatically jump to buffer when it is uniquely selected (default: "off")" +python.go.buffer_number = "display buffer number (default: "on")" +python.go.color_name = "color for buffer name (not selected) (default: "black,cyan")" +python.go.color_name_highlight = "color for highlight in buffer name (not selected) (default: "red,cyan")" +python.go.color_name_highlight_selected = "color for highlight in a selected buffer name (default: "red,brown")" +python.go.color_name_selected = "color for a selected buffer name (default: "black,brown")" +python.go.color_number = "color for buffer number (not selected) (default: "yellow,magenta")" +python.go.color_number_selected = "color for selected buffer number (default: "yellow,red")" +python.go.fuzzy_search = "search buffer matches using approximation (default: "off")" +python.go.message = "message to display before list of buffers (default: "Go to: ")" +python.go.short_name = "display and search in short names instead of buffer name (default: "off")" +python.go.sort = "comma-separated list of keys to sort buffers (the order is important, sorts are performed in the given order): name = sort by name (or short name), (default: "number,beginning")" +python.go.use_core_instead_weechat = "use name "core" instead of "weechat" for core buffer (default: "off")" diff --git a/config/weechat/python.conf b/config/weechat/python.conf new file mode 100644 index 00000000..187b778d --- /dev/null +++ b/config/weechat/python.conf @@ -0,0 +1,14 @@ +# +# weechat -- python.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +check_license = off +eval_keep_context = on diff --git a/config/weechat/python/aesthetic.py b/config/weechat/python/aesthetic.py new file mode 100644 index 00000000..988071d6 --- /dev/null +++ b/config/weechat/python/aesthetic.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# +# Script Name: aesthetic.py +# Script Author: Wojciech Siewierski +# Script License: GPL3 +# Contact: vifon @ irc.freenode.net + +SCRIPT_NAME = 'aesthetic' +SCRIPT_AUTHOR = 'Wojciech Siewierski' +SCRIPT_VERSION = '1.0.6' +SCRIPT_LICENSE = 'GPL3' +SCRIPT_DESC = 'Make messages more A E S T H E T I C A L L Y pleasing.' + +import_ok = True + +try: + import weechat +except ImportError: + print('This script must be run under WeeChat') + print('You can obtain a copy of WeeChat, for free, at https://weechat.org') + import_ok = False + +weechat_version = 0 + +import shlex +import sys + +def aesthetic_(args): + for arg in args: + try: + arg = arg.decode('utf8') + except AttributeError: + pass + yield " ".join(arg.upper()) + for n, char in enumerate(arg[1:]): + yield " ".join(" "*(n+1)).join(char.upper()*2) + +def aesthetic(args): + if sys.version_info < (3,): + return (x.encode('utf8') for x in aesthetic_(args)) + else: + return aesthetic_(args) + +def aesthetic_cb(data, buffer, args): + for x in aesthetic(shlex.split(args)): + weechat.command(buffer, x) + return weechat.WEECHAT_RC_OK + +if __name__ == "__main__" and import_ok: + if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): + weechat_version = weechat.info_get("version_number", "") or 0 + weechat.hook_command( + "aesthetic", + """Format a message like this: + +E X A M P L E +X X +A A +M M +P P +L L +E E + +Each argument is formatted separately, use sh-like quotes for grouping. For example '/aesthetic foo bar' will send two such blocks while '/aesthetic "foo bar"' would send one larger one. + +Use with care to not cause undesirable message spam.""", + "message", "", + "", + "aesthetic_cb", "" + ) diff --git a/config/weechat/python/anotify.py b/config/weechat/python/anotify.py new file mode 100644 index 00000000..88e020e5 --- /dev/null +++ b/config/weechat/python/anotify.py @@ -0,0 +1,472 @@ +# -*- coding: utf-8 -*- +# +# anotify.py +# Copyright (c) 2012 magnific0 +# +# based on: +# growl.py +# Copyright (c) 2011 Sorin Ionescu +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +SCRIPT_NAME = 'anotify' +SCRIPT_AUTHOR = 'magnific0' +SCRIPT_VERSION = '1.0.2' +SCRIPT_LICENSE = 'MIT' +SCRIPT_DESC = 'Sends libnotify notifications upon events.' + + +# Changelog +# 2014-05-10: v1.0.1 Change hook_print callback argument type of +# displayed/highlight (WeeChat >= 1.0) +# 2012-09-20: v1.0.0 Forked from original and adapted for libnotify. + +# ----------------------------------------------------------------------------- +# Settings +# ----------------------------------------------------------------------------- +SETTINGS = { + 'show_public_message': 'off', + 'show_private_message': 'on', + 'show_public_action_message': 'off', + 'show_private_action_message': 'on', + 'show_notice_message': 'off', + 'show_invite_message': 'on', + 'show_highlighted_message': 'on', + 'show_server': 'on', + 'show_channel_topic': 'on', + 'show_dcc': 'on', + 'show_upgrade_ended': 'on', + 'sticky': 'off', + 'sticky_away': 'on', + 'icon': '/usr/share/pixmaps/weechat.xpm', +} + + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +try: + import re + import os + import weechat + import notify2 + IMPORT_OK = True +except ImportError as error: + IMPORT_OK = False + if str(error).find('weechat') != -1: + print('This script must be run under WeeChat.') + print('Get WeeChat at http://www.weechat.org.') + else: + weechat.prnt('', 'anotify: {0}'.format(error)) + +# ----------------------------------------------------------------------------- +# Globals +# ----------------------------------------------------------------------------- +TAGGED_MESSAGES = { + 'public message or action': set(['irc_privmsg', 'notify_message']), + 'private message or action': set(['irc_privmsg', 'notify_private']), + 'notice message': set(['irc_notice', 'notify_private']), + 'invite message': set(['irc_invite', 'notify_highlight']), + 'channel topic': set(['irc_topic', ]), + #'away status': set(['away_info', ]), +} + + +UNTAGGED_MESSAGES = { + 'away status': + re.compile(r'^You ((\w+).){2,3}marked as being away', re.UNICODE), + 'dcc chat request': + re.compile(r'^xfer: incoming chat request from (\w+)', re.UNICODE), + 'dcc chat closed': + re.compile(r'^xfer: chat closed with (\w+)', re.UNICODE), + 'dcc get request': + re.compile( + r'^xfer: incoming file from (\w+) [^:]+: ((?:,\w|[^,])+),', + re.UNICODE), + 'dcc get completed': + re.compile(r'^xfer: file ([^\s]+) received from \w+: OK', re.UNICODE), + 'dcc get failed': + re.compile( + r'^xfer: file ([^\s]+) received from \w+: FAILED', + re.UNICODE), + 'dcc send completed': + re.compile(r'^xfer: file ([^\s]+) sent to \w+: OK', re.UNICODE), + 'dcc send failed': + re.compile(r'^xfer: file ([^\s]+) sent to \w+: FAILED', re.UNICODE), +} + + +DISPATCH_TABLE = { + 'away status': 'set_away_status', + 'public message or action': 'notify_public_message_or_action', + 'private message or action': 'notify_private_message_or_action', + 'notice message': 'notify_notice_message', + 'invite message': 'notify_invite_message', + 'channel topic': 'notify_channel_topic', + 'dcc chat request': 'notify_dcc_chat_request', + 'dcc chat closed': 'notify_dcc_chat_closed', + 'dcc get request': 'notify_dcc_get_request', + 'dcc get completed': 'notify_dcc_get_completed', + 'dcc get failed': 'notify_dcc_get_failed', + 'dcc send completed': 'notify_dcc_send_completed', + 'dcc send failed': 'notify_dcc_send_failed', +} + + +STATE = { + 'icon': None, + 'is_away': False +} + + +# ----------------------------------------------------------------------------- +# Notifiers +# ----------------------------------------------------------------------------- +def cb_irc_server_connected(data, signal, signal_data): + '''Notify when connected to IRC server.''' + if weechat.config_get_plugin('show_server') == 'on': + a_notify( + 'Server', + 'Server Connected', + 'Connected to network {0}.'.format(signal_data)) + return weechat.WEECHAT_RC_OK + + +def cb_irc_server_disconnected(data, signal, signal_data): + '''Notify when disconnected to IRC server.''' + if weechat.config_get_plugin('show_server') == 'on': + a_notify( + 'Server', + 'Server Disconnected', + 'Disconnected from network {0}.'.format(signal_data)) + return weechat.WEECHAT_RC_OK + + +def cb_notify_upgrade_ended(data, signal, signal_data): + '''Notify on end of WeeChat upgrade.''' + if weechat.config_get_plugin('show_upgrade_ended') == 'on': + a_notify( + 'WeeChat', + 'WeeChat Upgraded', + 'WeeChat has been upgraded.') + return weechat.WEECHAT_RC_OK + + +def notify_highlighted_message(prefix, message): + '''Notify on highlighted message.''' + if weechat.config_get_plugin("show_highlighted_message") == "on": + a_notify( + 'Highlight', + 'Highlighted Message', + "{0}: {1}".format(prefix, message), + priority=notify2.URGENCY_CRITICAL) + + +def notify_public_message_or_action(prefix, message, highlighted): + '''Notify on public message or action.''' + if prefix == ' *': + regex = re.compile(r'^(\w+) (.+)$', re.UNICODE) + match = regex.match(message) + if match: + prefix = match.group(1) + message = match.group(2) + notify_public_action_message(prefix, message, highlighted) + else: + if highlighted: + notify_highlighted_message(prefix, message) + elif weechat.config_get_plugin("show_public_message") == "on": + a_notify( + 'Public', + 'Public Message', + '{0}: {1}'.format(prefix, message)) + + +def notify_private_message_or_action(prefix, message, highlighted): + '''Notify on private message or action.''' + regex = re.compile(r'^CTCP_MESSAGE.+?ACTION (.+)$', re.UNICODE) + match = regex.match(message) + if match: + notify_private_action_message(prefix, match.group(1), highlighted) + else: + if prefix == ' *': + regex = re.compile(r'^(\w+) (.+)$', re.UNICODE) + match = regex.match(message) + if match: + prefix = match.group(1) + message = match.group(2) + notify_private_action_message(prefix, message, highlighted) + else: + if highlighted: + notify_highlighted_message(prefix, message) + elif weechat.config_get_plugin("show_private_message") == "on": + a_notify( + 'Private', + 'Private Message', + '{0}: {1}'.format(prefix, message)) + + +def notify_public_action_message(prefix, message, highlighted): + '''Notify on public action message.''' + if highlighted: + notify_highlighted_message(prefix, message) + elif weechat.config_get_plugin("show_public_action_message") == "on": + a_notify( + 'Action', + 'Public Action Message', + '{0}: {1}'.format(prefix, message), + priority=notify2.URGENCY_NORMAL) + + +def notify_private_action_message(prefix, message, highlighted): + '''Notify on private action message.''' + if highlighted: + notify_highlighted_message(prefix, message) + elif weechat.config_get_plugin("show_private_action_message") == "on": + a_notify( + 'Action', + 'Private Action Message', + '{0}: {1}'.format(prefix, message), + priority=notify2.URGENCY_NORMAL) + + +def notify_notice_message(prefix, message, highlighted): + '''Notify on notice message.''' + regex = re.compile(r'^([^\s]*) [^:]*: (.+)$', re.UNICODE) + match = regex.match(message) + if match: + prefix = match.group(1) + message = match.group(2) + if highlighted: + notify_highlighted_message(prefix, message) + elif weechat.config_get_plugin("show_notice_message") == "on": + a_notify( + 'Notice', + 'Notice Message', + '{0}: {1}'.format(prefix, message)) + + +def notify_invite_message(prefix, message, highlighted): + '''Notify on channel invitation message.''' + if weechat.config_get_plugin("show_invite_message") == "on": + regex = re.compile( + r'^You have been invited to ([^\s]+) by ([^\s]+)$', re.UNICODE) + match = regex.match(message) + if match: + channel = match.group(1) + nick = match.group(2) + a_notify( + 'Invite', + 'Channel Invitation', + '{0} has invited you to join {1}.'.format(nick, channel)) + + +def notify_channel_topic(prefix, message, highlighted): + '''Notify on channel topic change.''' + if weechat.config_get_plugin("show_channel_topic") == "on": + regex = re.compile( + r'^\w+ has (?:changed|unset) topic for ([^\s]+)' + + '(?:(?: from "(?:(?:"\w|[^"])+)")? to "((?:"\w|[^"])+)")?', + re.UNICODE) + match = regex.match(message) + if match: + channel = match.group(1) + topic = match.group(2) or '' + a_notify( + 'Channel', + 'Channel Topic', + "{0}: {1}".format(channel, topic)) + + +def notify_dcc_chat_request(match): + '''Notify on DCC chat request.''' + if weechat.config_get_plugin("show_dcc") == "on": + nick = match.group(1) + a_notify( + 'DCC', + 'Direct Chat Request', + '{0} wants to chat directly.'.format(nick)) + + +def notify_dcc_chat_closed(match): + '''Notify on DCC chat termination.''' + if weechat.config_get_plugin("show_dcc") == "on": + nick = match.group(1) + a_notify( + 'DCC', + 'Direct Chat Ended', + 'Direct chat with {0} has ended.'.format(nick)) + + +def notify_dcc_get_request(match): + 'Notify on DCC get request.' + if weechat.config_get_plugin("show_dcc") == "on": + nick = match.group(1) + file_name = match.group(2) + a_notify( + 'DCC', + 'File Transfer Request', + '{0} wants to send you {1}.'.format(nick, file_name)) + + +def notify_dcc_get_completed(match): + 'Notify on DCC get completion.' + if weechat.config_get_plugin("show_dcc") == "on": + file_name = match.group(1) + a_notify('DCC', 'Download Complete', file_name) + + +def notify_dcc_get_failed(match): + 'Notify on DCC get failure.' + if weechat.config_get_plugin("show_dcc") == "on": + file_name = match.group(1) + a_notify('DCC', 'Download Failed', file_name) + + +def notify_dcc_send_completed(match): + 'Notify on DCC send completion.' + if weechat.config_get_plugin("show_dcc") == "on": + file_name = match.group(1) + a_notify('DCC', 'Upload Complete', file_name) + + +def notify_dcc_send_failed(match): + 'Notify on DCC send failure.' + if weechat.config_get_plugin("show_dcc") == "on": + file_name = match.group(1) + a_notify('DCC', 'Upload Failed', file_name) + + +# ----------------------------------------------------------------------------- +# Utility +# ----------------------------------------------------------------------------- +def set_away_status(match): + status = match.group(1) + if status == 'been ': + STATE['is_away'] = True + if status == 'longer ': + STATE['is_away'] = False + + +def cb_process_message( + data, + wbuffer, + date, + tags, + displayed, + highlight, + prefix, + message +): + '''Delegates incoming messages to appropriate handlers.''' + tags = set(tags.split(',')) + functions = globals() + is_public_message = tags.issuperset( + TAGGED_MESSAGES['public message or action']) + buffer_name = weechat.buffer_get_string(wbuffer, 'name') + dcc_buffer_regex = re.compile(r'^irc_dcc\.', re.UNICODE) + dcc_buffer_match = dcc_buffer_regex.match(buffer_name) + highlighted = False + if int(highlight): + highlighted = True + # Private DCC message identifies itself as public. + if is_public_message and dcc_buffer_match: + notify_private_message_or_action(prefix, message, highlighted) + return weechat.WEECHAT_RC_OK + # Pass identified, untagged message to its designated function. + for key, value in UNTAGGED_MESSAGES.items(): + match = value.match(message) + if match: + functions[DISPATCH_TABLE[key]](match) + return weechat.WEECHAT_RC_OK + # Pass identified, tagged message to its designated function. + for key, value in TAGGED_MESSAGES.items(): + if tags.issuperset(value): + functions[DISPATCH_TABLE[key]](prefix, message, highlighted) + return weechat.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK + + +def a_notify(notification, title, description, priority=notify2.URGENCY_LOW): + '''Returns whether notifications should be sticky.''' + is_away = STATE['is_away'] + icon = STATE['icon'] + time_out = 5000 + if weechat.config_get_plugin('sticky') == 'on': + time_out = 0 + if weechat.config_get_plugin('sticky_away') == 'on' and is_away: + time_out = 0 + try: + notify2.init("wee-notifier") + wn = notify2.Notification(title, description, icon) + wn.set_urgency(priority) + wn.set_timeout(time_out) + wn.show() + except Exception as error: + weechat.prnt('', 'anotify: {0}'.format(error)) + + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- +def main(): + '''Sets up WeeChat notifications.''' + # Initialize options. + for option, value in SETTINGS.items(): + if not weechat.config_is_set_plugin(option): + weechat.config_set_plugin(option, value) + # Initialize. + name = "WeeChat" + icon = "/usr/share/pixmaps/weechat.xpm" + notifications = [ + 'Public', + 'Private', + 'Action', + 'Notice', + 'Invite', + 'Highlight', + 'Server', + 'Channel', + 'DCC', + 'WeeChat' + ] + STATE['icon'] = icon + # Register hooks. + weechat.hook_signal( + 'irc_server_connected', + 'cb_irc_server_connected', + '') + weechat.hook_signal( + 'irc_server_disconnected', + 'cb_irc_server_disconnected', + '') + weechat.hook_signal('upgrade_ended', 'cb_upgrade_ended', '') + weechat.hook_print('', '', '', 1, 'cb_process_message', '') + + +if __name__ == '__main__' and IMPORT_OK and weechat.register( + SCRIPT_NAME, + SCRIPT_AUTHOR, + SCRIPT_VERSION, + SCRIPT_LICENSE, + SCRIPT_DESC, + '', + '' +): + main() diff --git a/config/weechat/python/autoload/aesthetic.py b/config/weechat/python/autoload/aesthetic.py new file mode 120000 index 00000000..9c60e1b2 --- /dev/null +++ b/config/weechat/python/autoload/aesthetic.py @@ -0,0 +1 @@ +../aesthetic.py \ No newline at end of file diff --git a/config/weechat/python/autoload/anotify.py b/config/weechat/python/autoload/anotify.py new file mode 120000 index 00000000..1517fefe --- /dev/null +++ b/config/weechat/python/autoload/anotify.py @@ -0,0 +1 @@ +../anotify.py \ No newline at end of file diff --git a/config/weechat/python/autoload/autosort.py b/config/weechat/python/autoload/autosort.py new file mode 120000 index 00000000..1850897f --- /dev/null +++ b/config/weechat/python/autoload/autosort.py @@ -0,0 +1 @@ +../autosort.py \ No newline at end of file diff --git a/config/weechat/python/autoload/colorize_nicks.py b/config/weechat/python/autoload/colorize_nicks.py new file mode 120000 index 00000000..3ee34e96 --- /dev/null +++ b/config/weechat/python/autoload/colorize_nicks.py @@ -0,0 +1 @@ +../colorize_nicks.py \ No newline at end of file diff --git a/config/weechat/python/autoload/go.py b/config/weechat/python/autoload/go.py new file mode 120000 index 00000000..bdfb7ddf --- /dev/null +++ b/config/weechat/python/autoload/go.py @@ -0,0 +1 @@ +../go.py \ No newline at end of file diff --git a/config/weechat/python/autosort.py b/config/weechat/python/autosort.py new file mode 100644 index 00000000..4312cda7 --- /dev/null +++ b/config/weechat/python/autosort.py @@ -0,0 +1,1075 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Maarten de Vries +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# Autosort automatically keeps your buffers sorted and grouped by server. +# You can define your own sorting rules. See /help autosort for more details. +# +# https://github.com/de-vri-es/weechat-autosort +# + +# +# Changelog: +# 3.9: +# * Remove `buffers.pl` from recommended settings. +# 3,8: +# * Fix relative sorting on script name in default rules. +# * Document a useful property of stable sort algorithms. +# 3.7: +# * Make default rules work with bitlbee, matrix and slack. +# 3.6: +# * Add more documentation on provided info hooks. +# 3.5: +# * Add ${info:autosort_escape,...} to escape arguments for other info hooks. +# 3.4: +# * Fix rate-limit of sorting to prevent high CPU load and lock-ups. +# * Fix bug in parsing empty arguments for info hooks. +# * Add debug_log option to aid with debugging. +# * Correct a few typos. +# 3.3: +# * Fix the /autosort debug command for unicode. +# * Update the default rules to work better with Slack. +# 3.2: +# * Fix python3 compatiblity. +# 3.1: +# * Use colors to format the help text. +# 3.0: +# * Switch to evaluated expressions for sorting. +# * Add `/autosort debug` command. +# * Add ${info:autosort_replace,from,to,text} to replace substrings in sort rules. +# * Add ${info:autosort_order,value,first,second,third} to ease writing sort rules. +# * Make tab completion context aware. +# 2.8: +# * Fix compatibility with python 3 regarding unicode handling. +# 2.7: +# * Fix sorting of buffers with spaces in their name. +# 2.6: +# * Ignore case in rules when doing case insensitive sorting. +# 2.5: +# * Fix handling unicode buffer names. +# * Add hint to set irc.look.server_buffer to independent and buffers.look.indenting to on. +# 2.4: +# * Make script python3 compatible. +# 2.3: +# * Fix sorting items without score last (regressed in 2.2). +# 2.2: +# * Add configuration option for signals that trigger a sort. +# * Add command to manually trigger a sort (/autosort sort). +# * Add replacement patterns to apply before sorting. +# 2.1: +# * Fix some minor style issues. +# 2.0: +# * Allow for custom sort rules. +# + + +import json +import math +import re +import sys +import time +import weechat + +SCRIPT_NAME = 'autosort' +SCRIPT_AUTHOR = 'Maarten de Vries ' +SCRIPT_VERSION = '3.9' +SCRIPT_LICENSE = 'GPL3' +SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.' + + +config = None +hooks = [] +signal_delay_timer = None +sort_limit_timer = None +sort_queued = False + + +# Make sure that unicode, bytes and str are always available in python2 and 3. +# For python 2, str == bytes +# For python 3, str == unicode +if sys.version_info[0] >= 3: + unicode = str + +def ensure_str(input): + ''' + Make sure the given type if the correct string type for the current python version. + That means bytes for python2 and unicode for python3. + ''' + if not isinstance(input, str): + if isinstance(input, bytes): + return input.encode('utf-8') + if isinstance(input, unicode): + return input.decode('utf-8') + return input + + +if hasattr(time, 'perf_counter'): + perf_counter = time.perf_counter +else: + perf_counter = time.clock + +def casefold(string): + if hasattr(string, 'casefold'): return string.casefold() + # Fall back to lowercasing for python2. + return string.lower() + +def list_swap(values, a, b): + values[a], values[b] = values[b], values[a] + +def list_move(values, old_index, new_index): + values.insert(new_index, values.pop(old_index)) + +def list_find(collection, value): + for i, elem in enumerate(collection): + if elem == value: return i + return None + +class HumanReadableError(Exception): + pass + +def parse_int(arg, arg_name = 'argument'): + ''' Parse an integer and provide a more human readable error. ''' + arg = arg.strip() + try: + return int(arg) + except ValueError: + raise HumanReadableError('Invalid {0}: expected integer, got "{1}".'.format(arg_name, arg)) + +def decode_rules(blob): + parsed = json.loads(blob) + if not isinstance(parsed, list): + log('Malformed rules, expected a JSON encoded list of strings, but got a {0}. No rules have been loaded. Please fix the setting manually.'.format(type(parsed))) + return [] + + for i, entry in enumerate(parsed): + if not isinstance(entry, (str, unicode)): + log('Rule #{0} is not a string but a {1}. No rules have been loaded. Please fix the setting manually.'.format(i, type(entry))) + return [] + + return parsed + +def decode_helpers(blob): + parsed = json.loads(blob) + if not isinstance(parsed, dict): + log('Malformed helpers, expected a JSON encoded dictionary but got a {0}. No helpers have been loaded. Please fix the setting manually.'.format(type(parsed))) + return {} + + for key, value in parsed.items(): + if not isinstance(value, (str, unicode)): + log('Helper "{0}" is not a string but a {1}. No helpers have been loaded. Please fix setting manually.'.format(key, type(value))) + return {} + return parsed + +class Config: + ''' The autosort configuration. ''' + + default_rules = json.dumps([ + '${core_first}', + '${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}', + '${script_or_plugin}', + '${irc_raw_first}', + '${server}', + '${info:autosort_order,${type},server,*,channel,private}', + '${hashless_name}', + '${buffer.full_name}', + ]) + + default_helpers = json.dumps({ + 'core_first': '${if:${buffer.full_name}!=core.weechat}', + 'irc_raw_first': '${if:${buffer.full_name}!=irc.irc_raw}', + 'irc_raw_last': '${if:${buffer.full_name}==irc.irc_raw}', + 'hashless_name': '${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}', + 'script_or_plugin': '${if:${script_name}?${script_name}:${plugin}}', + }) + + default_signal_delay = 5 + default_sort_limit = 100 + + default_signals = 'buffer_opened buffer_merged buffer_unmerged buffer_renamed' + + def __init__(self, filename): + ''' Initialize the configuration. ''' + + self.filename = filename + self.config_file = weechat.config_new(self.filename, '', '') + self.sorting_section = None + self.v3_section = None + + self.case_sensitive = False + self.rules = [] + self.helpers = {} + self.signals = [] + self.signal_delay = Config.default_signal_delay, + self.sort_limit = Config.default_sort_limit, + self.sort_on_config = True + self.debug_log = False + + self.__case_sensitive = None + self.__rules = None + self.__helpers = None + self.__signals = None + self.__signal_delay = None + self.__sort_limit = None + self.__sort_on_config = None + self.__debug_log = None + + if not self.config_file: + log('Failed to initialize configuration file "{0}".'.format(self.filename)) + return + + self.sorting_section = weechat.config_new_section(self.config_file, 'sorting', False, False, '', '', '', '', '', '', '', '', '', '') + self.v3_section = weechat.config_new_section(self.config_file, 'v3', False, False, '', '', '', '', '', '', '', '', '', '') + + if not self.sorting_section: + log('Failed to initialize section "sorting" of configuration file.') + weechat.config_free(self.config_file) + return + + self.__case_sensitive = weechat.config_new_option( + self.config_file, self.sorting_section, + 'case_sensitive', 'boolean', + 'If this option is on, sorting is case sensitive.', + '', 0, 0, 'off', 'off', 0, + '', '', '', '', '', '' + ) + + weechat.config_new_option( + self.config_file, self.sorting_section, + 'rules', 'string', + 'Sort rules used by autosort v2.x and below. Not used by autosort anymore.', + '', 0, 0, '', '', 0, + '', '', '', '', '', '' + ) + + weechat.config_new_option( + self.config_file, self.sorting_section, + 'replacements', 'string', + 'Replacement patterns used by autosort v2.x and below. Not used by autosort anymore.', + '', 0, 0, '', '', 0, + '', '', '', '', '', '' + ) + + self.__rules = weechat.config_new_option( + self.config_file, self.v3_section, + 'rules', 'string', + 'An ordered list of sorting rules encoded as JSON. See /help autosort for commands to manipulate these rules.', + '', 0, 0, Config.default_rules, Config.default_rules, 0, + '', '', '', '', '', '' + ) + + self.__helpers = weechat.config_new_option( + self.config_file, self.v3_section, + 'helpers', 'string', + 'A dictionary helper variables to use in the sorting rules, encoded as JSON. See /help autosort for commands to manipulate these helpers.', + '', 0, 0, Config.default_helpers, Config.default_helpers, 0, + '', '', '', '', '', '' + ) + + self.__signals = weechat.config_new_option( + self.config_file, self.sorting_section, + 'signals', 'string', + 'A space separated list of signals that will cause autosort to resort your buffer list.', + '', 0, 0, Config.default_signals, Config.default_signals, 0, + '', '', '', '', '', '' + ) + + self.__signal_delay = weechat.config_new_option( + self.config_file, self.sorting_section, + 'signal_delay', 'integer', + 'Delay in milliseconds to wait after a signal before sorting the buffer list. This prevents triggering many times if multiple signals arrive in a short time. It can also be needed to wait for buffer localvars to be available.', + '', 0, 1000, str(Config.default_signal_delay), str(Config.default_signal_delay), 0, + '', '', '', '', '', '' + ) + + self.__sort_limit = weechat.config_new_option( + self.config_file, self.sorting_section, + 'sort_limit', 'integer', + 'Minimum delay in milliseconds to wait after sorting before signals can trigger a sort again. This is effectively a rate limit on sorting. Keeping signal_delay low while setting this higher can reduce excessive sorting without a long initial delay.', + '', 0, 1000, str(Config.default_sort_limit), str(Config.default_sort_limit), 0, + '', '', '', '', '', '' + ) + + self.__sort_on_config = weechat.config_new_option( + self.config_file, self.sorting_section, + 'sort_on_config_change', 'boolean', + 'Decides if the buffer list should be sorted when autosort configuration changes.', + '', 0, 0, 'on', 'on', 0, + '', '', '', '', '', '' + ) + + self.__debug_log = weechat.config_new_option( + self.config_file, self.sorting_section, + 'debug_log', 'boolean', + 'If enabled, print more debug messages. Not recommended for normal usage.', + '', 0, 0, 'off', 'off', 0, + '', '', '', '', '', '' + ) + + if weechat.config_read(self.config_file) != weechat.WEECHAT_RC_OK: + log('Failed to load configuration file.') + + if weechat.config_write(self.config_file) != weechat.WEECHAT_RC_OK: + log('Failed to write configuration file.') + + self.reload() + + def reload(self): + ''' Load configuration variables. ''' + + self.case_sensitive = weechat.config_boolean(self.__case_sensitive) + + rules_blob = weechat.config_string(self.__rules) + helpers_blob = weechat.config_string(self.__helpers) + signals_blob = weechat.config_string(self.__signals) + + self.rules = decode_rules(rules_blob) + self.helpers = decode_helpers(helpers_blob) + self.signals = signals_blob.split() + self.signal_delay = weechat.config_integer(self.__signal_delay) + self.sort_limit = weechat.config_integer(self.__sort_limit) + self.sort_on_config = weechat.config_boolean(self.__sort_on_config) + self.debug_log = weechat.config_boolean(self.__debug_log) + + def save_rules(self, run_callback = True): + ''' Save the current rules to the configuration. ''' + weechat.config_option_set(self.__rules, json.dumps(self.rules), run_callback) + + def save_helpers(self, run_callback = True): + ''' Save the current helpers to the configuration. ''' + weechat.config_option_set(self.__helpers, json.dumps(self.helpers), run_callback) + + +def pad(sequence, length, padding = None): + ''' Pad a list until is has a certain length. ''' + return sequence + [padding] * max(0, (length - len(sequence))) + +def log(message, buffer = 'NULL'): + weechat.prnt(buffer, 'autosort: {0}'.format(message)) + +def debug(message, buffer = 'NULL'): + if config.debug_log: + weechat.prnt(buffer, 'autosort: debug: {0}'.format(message)) + +def get_buffers(): + ''' Get a list of all the buffers in weechat. ''' + hdata = weechat.hdata_get('buffer') + buffer = weechat.hdata_get_list(hdata, "gui_buffers"); + + result = [] + while buffer: + number = weechat.hdata_integer(hdata, buffer, 'number') + result.append((number, buffer)) + buffer = weechat.hdata_pointer(hdata, buffer, 'next_buffer') + return hdata, result + +class MergedBuffers(list): + """ A list of merged buffers, possibly of size 1. """ + def __init__(self, number): + super(MergedBuffers, self).__init__() + self.number = number + +def merge_buffer_list(buffers): + ''' + Group merged buffers together. + The output is a list of MergedBuffers. + ''' + if not buffers: return [] + result = {} + for number, buffer in buffers: + if number not in result: result[number] = MergedBuffers(number) + result[number].append(buffer) + return result.values() + +def sort_buffers(hdata, buffers, rules, helpers, case_sensitive): + for merged in buffers: + for buffer in merged: + name = weechat.hdata_string(hdata, buffer, 'name') + + return sorted(buffers, key=merged_sort_key(rules, helpers, case_sensitive)) + +def buffer_sort_key(rules, helpers, case_sensitive): + ''' Create a sort key function for a list of lists of merged buffers. ''' + def key(buffer): + extra_vars = {} + for helper_name, helper in sorted(helpers.items()): + expanded = weechat.string_eval_expression(helper, {"buffer": buffer}, {}, {}) + extra_vars[helper_name] = expanded if case_sensitive else casefold(expanded) + result = [] + for rule in rules: + expanded = weechat.string_eval_expression(rule, {"buffer": buffer}, extra_vars, {}) + result.append(expanded if case_sensitive else casefold(expanded)) + return result + + return key + +def merged_sort_key(rules, helpers, case_sensitive): + buffer_key = buffer_sort_key(rules, helpers, case_sensitive) + def key(merged): + best = None + for buffer in merged: + this = buffer_key(buffer) + if best is None or this < best: best = this + return best + return key + +def apply_buffer_order(buffers): + ''' Sort the buffers in weechat according to the given order. ''' + for i, buffer in enumerate(buffers): + weechat.buffer_set(buffer[0], "number", str(i + 1)) + +def split_args(args, expected, optional = 0): + ''' Split an argument string in the desired number of arguments. ''' + split = args.split(' ', expected - 1) + if (len(split) < expected): + raise HumanReadableError('Expected at least {0} arguments, got {1}.'.format(expected, len(split))) + return split[:-1] + pad(split[-1].split(' ', optional), optional + 1, '') + +def do_sort(verbose = False): + start = perf_counter() + + hdata, buffers = get_buffers() + buffers = merge_buffer_list(buffers) + buffers = sort_buffers(hdata, buffers, config.rules, config.helpers, config.case_sensitive) + apply_buffer_order(buffers) + + elapsed = perf_counter() - start + if verbose: + log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + else: + debug("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + +def command_sort(buffer, command, args): + ''' Sort the buffers and print a confirmation. ''' + do_sort(True) + return weechat.WEECHAT_RC_OK + +def command_debug(buffer, command, args): + hdata, buffers = get_buffers() + buffers = merge_buffer_list(buffers) + + # Show evaluation results. + log('Individual evaluation results:') + start = perf_counter() + key = buffer_sort_key(config.rules, config.helpers, config.case_sensitive) + results = [] + for merged in buffers: + for buffer in merged: + fullname = weechat.hdata_string(hdata, buffer, 'full_name') + results.append((fullname, key(buffer))) + elapsed = perf_counter() - start + + for fullname, result in results: + fullname = ensure_str(fullname) + result = [ensure_str(x) for x in result] + log('{0}: {1}'.format(fullname, result)) + log('Computing evaluation results took {0:.4f} seconds.'.format(elapsed)) + + return weechat.WEECHAT_RC_OK + +def command_rule_list(buffer, command, args): + ''' Show the list of sorting rules. ''' + output = 'Sorting rules:\n' + for i, rule in enumerate(config.rules): + output += ' {0}: {1}\n'.format(i, rule) + if not len(config.rules): + output += ' No sorting rules configured.\n' + log(output ) + + return weechat.WEECHAT_RC_OK + + +def command_rule_add(buffer, command, args): + ''' Add a rule to the rule list. ''' + config.rules.append(args) + config.save_rules() + command_rule_list(buffer, command, '') + + return weechat.WEECHAT_RC_OK + + +def command_rule_insert(buffer, command, args): + ''' Insert a rule at the desired position in the rule list. ''' + index, rule = split_args(args, 2) + index = parse_int(index, 'index') + + config.rules.insert(index, rule) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_update(buffer, command, args): + ''' Update a rule in the rule list. ''' + index, rule = split_args(args, 2) + index = parse_int(index, 'index') + + config.rules[index] = rule + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_delete(buffer, command, args): + ''' Delete a rule from the rule list. ''' + index = args.strip() + index = parse_int(index, 'index') + + config.rules.pop(index) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_move(buffer, command, args): + ''' Move a rule to a new position. ''' + index_a, index_b = split_args(args, 2) + index_a = parse_int(index_a, 'index') + index_b = parse_int(index_b, 'index') + + list_move(config.rules, index_a, index_b) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_swap(buffer, command, args): + ''' Swap two rules. ''' + index_a, index_b = split_args(args, 2) + index_a = parse_int(index_a, 'index') + index_b = parse_int(index_b, 'index') + + list_swap(config.rules, index_a, index_b) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_helper_list(buffer, command, args): + ''' Show the list of helpers. ''' + output = 'Helper variables:\n' + + width = max(map(lambda x: len(x) if len(x) <= 30 else 0, config.helpers.keys())) + + for name, expression in sorted(config.helpers.items()): + output += ' {0:>{width}}: {1}\n'.format(name, expression, width=width) + if not len(config.helpers): + output += ' No helper variables configured.' + log(output) + + return weechat.WEECHAT_RC_OK + + +def command_helper_set(buffer, command, args): + ''' Add/update a helper to the helper list. ''' + name, expression = split_args(args, 2) + + config.helpers[name] = expression + config.save_helpers() + command_helper_list(buffer, command, '') + + return weechat.WEECHAT_RC_OK + +def command_helper_delete(buffer, command, args): + ''' Delete a helper from the helper list. ''' + name = args.strip() + + del config.helpers[name] + config.save_helpers() + command_helper_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_helper_rename(buffer, command, args): + ''' Rename a helper to a new position. ''' + old_name, new_name = split_args(args, 2) + + try: + config.helpers[new_name] = config.helpers[old_name] + del config.helpers[old_name] + except KeyError: + raise HumanReadableError('No such helper: {0}'.format(old_name)) + config.save_helpers() + command_helper_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_helper_swap(buffer, command, args): + ''' Swap two helpers. ''' + a, b = split_args(args, 2) + try: + config.helpers[b], config.helpers[a] = config.helpers[a], config.helpers[b] + except KeyError as e: + raise HumanReadableError('No such helper: {0}'.format(e.args[0])) + + config.helpers.swap(index_a, index_b) + config.save_helpers() + command_helper_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + +def call_command(buffer, command, args, subcommands): + ''' Call a subcommand from a dictionary. ''' + subcommand, tail = pad(args.split(' ', 1), 2, '') + subcommand = subcommand.strip() + if (subcommand == ''): + child = subcommands.get(' ') + else: + command = command + [subcommand] + child = subcommands.get(subcommand) + + if isinstance(child, dict): + return call_command(buffer, command, tail, child) + elif callable(child): + return child(buffer, command, tail) + + log('{0}: command not found'.format(' '.join(command))) + return weechat.WEECHAT_RC_ERROR + +def on_signal(data, signal, signal_data): + global signal_delay_timer + global sort_queued + + # If the sort limit timeout is started, we're in the hold-off time after sorting, just queue a sort. + if sort_limit_timer is not None: + if sort_queued: + debug('Signal {0} ignored, sort limit timeout is active and sort is already queued.'.format(signal)) + else: + debug('Signal {0} received but sort limit timeout is active, sort is now queued.'.format(signal)) + sort_queued = True + return weechat.WEECHAT_RC_OK + + # If the signal delay timeout is started, a signal was recently received, so ignore this signal. + if signal_delay_timer is not None: + debug('Signal {0} ignored, signal delay timeout active.'.format(signal)) + return weechat.WEECHAT_RC_OK + + # Otherwise, start the signal delay timeout. + debug('Signal {0} received, starting signal delay timeout of {1} ms.'.format(signal, config.signal_delay)) + weechat.hook_timer(config.signal_delay, 0, 1, "on_signal_delay_timeout", "") + return weechat.WEECHAT_RC_OK + +def on_signal_delay_timeout(pointer, remaining_calls): + """ Called when the signal_delay_timer triggers. """ + global signal_delay_timer + global sort_limit_timer + global sort_queued + + signal_delay_timer = None + + # If the sort limit timeout was started, we're still in the no-sort period, so just queue a sort. + if sort_limit_timer is not None: + debug('Signal delay timeout expired, but sort limit timeout is active, sort is now queued.') + sort_queued = True + return weechat.WEECHAT_RC_OK + + # Time to sort! + debug('Signal delay timeout expired, starting sort.') + do_sort() + + # Start the sort limit timeout if not disabled. + if config.sort_limit > 0: + debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit)) + sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "") + + return weechat.WEECHAT_RC_OK + +def on_sort_limit_timeout(pointer, remainin_calls): + """ Called when de sort_limit_timer triggers. """ + global sort_limit_timer + global sort_queued + + # If no signal was received during the timeout, we're done. + if not sort_queued: + debug('Sort limit timeout expired without receiving a signal.') + sort_limit_timer = None + return weechat.WEECHAT_RC_OK + + # Otherwise it's time to sort. + debug('Signal received during sort limit timeout, starting queued sort.') + do_sort() + sort_queued = False + + # Start the sort limit timeout again if not disabled. + if config.sort_limit > 0: + debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit)) + sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "") + + return weechat.WEECHAT_RC_OK + + +def apply_config(): + # Unhook all signals and hook the new ones. + for hook in hooks: + weechat.unhook(hook) + for signal in config.signals: + hooks.append(weechat.hook_signal(signal, 'on_signal', '')) + + if config.sort_on_config: + debug('Sorting because configuration changed.') + do_sort() + +def on_config_changed(*args, **kwargs): + ''' Called whenever the configuration changes. ''' + config.reload() + apply_config() + + return weechat.WEECHAT_RC_OK + +def parse_arg(args): + if not args: return '', None + + result = '' + escaped = False + for i, c in enumerate(args): + if not escaped: + if c == '\\': + escaped = True + continue + elif c == ',': + return result, args[i+1:] + result += c + escaped = False + return result, None + +def parse_args(args, max = None): + result = [] + i = 0 + while max is None or i < max: + i += 1 + arg, args = parse_arg(args) + if arg is None: break + result.append(arg) + if args is None: break + return result, args + +def on_info_escape(pointer, name, arguments): + result = '' + for c in arguments: + if c == '\\': + result += '\\\\' + elif c == ',': + result += '\\,' + else: + result +=c + return result + +def on_info_replace(pointer, name, arguments): + arguments, rest = parse_args(arguments, 3) + if rest or len(arguments) < 3: + log('usage: ${{info:{0},old,new,text}}'.format(name)) + return '' + old, new, text = arguments + + return text.replace(old, new) + +def on_info_order(pointer, name, arguments): + arguments, rest = parse_args(arguments) + if len(arguments) < 1: + log('usage: ${{info:{0},value,first,second,third,...}}'.format(name)) + return '' + + value = arguments[0] + keys = arguments[1:] + if not keys: return '0' + + # Find the value in the keys (or '*' if we can't find it) + result = list_find(keys, value) + if result is None: result = list_find(keys, '*') + if result is None: result = len(keys) + + # Pad result with leading zero to make sure string sorting works. + width = int(math.log10(len(keys))) + 1 + return '{0:0{1}}'.format(result, width) + + +def on_autosort_command(data, buffer, args): + ''' Called when the autosort command is invoked. ''' + try: + return call_command(buffer, ['/autosort'], args, { + ' ': command_sort, + 'sort': command_sort, + 'debug': command_debug, + + 'rules': { + ' ': command_rule_list, + 'list': command_rule_list, + 'add': command_rule_add, + 'insert': command_rule_insert, + 'update': command_rule_update, + 'delete': command_rule_delete, + 'move': command_rule_move, + 'swap': command_rule_swap, + }, + 'helpers': { + ' ': command_helper_list, + 'list': command_helper_list, + 'set': command_helper_set, + 'delete': command_helper_delete, + 'rename': command_helper_rename, + 'swap': command_helper_swap, + }, + }) + except HumanReadableError as e: + log(e) + return weechat.WEECHAT_RC_ERROR + +def add_completions(completion, words): + for word in words: + weechat.hook_completion_list_add(completion, word, 0, weechat.WEECHAT_LIST_POS_END) + +def autosort_complete_rules(words, completion): + if len(words) == 0: + add_completions(completion, ['add', 'delete', 'insert', 'list', 'move', 'swap', 'update']) + if len(words) == 1 and words[0] in ('delete', 'insert', 'move', 'swap', 'update'): + add_completions(completion, map(str, range(len(config.rules)))) + if len(words) == 2 and words[0] in ('move', 'swap'): + add_completions(completion, map(str, range(len(config.rules)))) + if len(words) == 2 and words[0] in ('update'): + try: + add_completions(completion, [config.rules[int(words[1])]]) + except KeyError: pass + except ValueError: pass + else: + add_completions(completion, ['']) + return weechat.WEECHAT_RC_OK + +def autosort_complete_helpers(words, completion): + if len(words) == 0: + add_completions(completion, ['delete', 'list', 'rename', 'set', 'swap']) + elif len(words) == 1 and words[0] in ('delete', 'rename', 'set', 'swap'): + add_completions(completion, sorted(config.helpers.keys())) + elif len(words) == 2 and words[0] == 'swap': + add_completions(completion, sorted(config.helpers.keys())) + elif len(words) == 2 and words[0] == 'rename': + add_completions(completion, sorted(config.helpers.keys())) + elif len(words) == 2 and words[0] == 'set': + try: + add_completions(completion, [config.helpers[words[1]]]) + except KeyError: pass + return weechat.WEECHAT_RC_OK + +def on_autosort_complete(data, name, buffer, completion): + cmdline = weechat.buffer_get_string(buffer, "input") + cursor = weechat.buffer_get_integer(buffer, "input_pos") + prefix = cmdline[:cursor] + words = prefix.split()[1:] + + # If the current word isn't finished yet, + # ignore it for coming up with completion suggestions. + if prefix[-1] != ' ': words = words[:-1] + + if len(words) == 0: + add_completions(completion, ['debug', 'helpers', 'rules', 'sort']) + elif words[0] == 'rules': + return autosort_complete_rules(words[1:], completion) + elif words[0] == 'helpers': + return autosort_complete_helpers(words[1:], completion) + return weechat.WEECHAT_RC_OK + +command_description = r'''{*white}# General commands{reset} + +{*white}/autosort {brown}sort{reset} +Manually trigger the buffer sorting. + +{*white}/autosort {brown}debug{reset} +Show the evaluation results of the sort rules for each buffer. + + +{*white}# Sorting rule commands{reset} + +{*white}/autosort{brown} rules list{reset} +Print the list of sort rules. + +{*white}/autosort {brown}rules add {cyan}{reset} +Add a new rule at the end of the list. + +{*white}/autosort {brown}rules insert {cyan} {reset} +Insert a new rule at the given index in the list. + +{*white}/autosort {brown}rules update {cyan} {reset} +Update a rule in the list with a new expression. + +{*white}/autosort {brown}rules delete {cyan} +Delete a rule from the list. + +{*white}/autosort {brown}rules move {cyan} {reset} +Move a rule from one position in the list to another. + +{*white}/autosort {brown}rules swap {cyan} {reset} +Swap two rules in the list + + +{*white}# Helper variable commands{reset} + +{*white}/autosort {brown}helpers list +Print the list of helper variables. + +{*white}/autosort {brown}helpers set {cyan} +Add or update a helper variable with the given name. + +{*white}/autosort {brown}helpers delete {cyan} +Delete a helper variable. + +{*white}/autosort {brown}helpers rename {cyan} +Rename a helper variable. + +{*white}/autosort {brown}helpers swap {cyan} +Swap the expressions of two helper variables in the list. + + +{*white}# Info hooks{reset} +Autosort comes with a number of info hooks to add some extra functionality to regular weechat eval strings. +Info hooks can be used in eval strings in the form of {cyan}${{info:some_hook,arguments}}{reset}. + +Commas and backslashes in arguments to autosort info hooks (except for {cyan}${{info:autosort_escape}}{reset}) must be escaped with a backslash. + +{*white}${{info:{brown}autosort_replace{white},{cyan}pattern{white},{cyan}replacement{white},{cyan}source{white}}}{reset} +Replace all occurrences of {cyan}pattern{reset} with {cyan}replacement{reset} in the string {cyan}source{reset}. +Can be used to ignore certain strings when sorting by replacing them with an empty string. + +For example: {cyan}${{info:autosort_replace,cat,dog,the dog is meowing}}{reset} expands to "the cat is meowing". + +{*white}${{info:{brown}autosort_order{white},{cyan}value{white},{cyan}option0{white},{cyan}option1{white},{cyan}option2{white},{cyan}...{white}}} +Generate a zero-padded number that corresponds to the index of {cyan}value{reset} in the list of options. +If one of the options is the special value {brown}*{reset}, then any value not explicitly mentioned will be sorted at that position. +Otherwise, any value that does not match an option is assigned the highest number available. +Can be used to easily sort buffers based on a manual sequence. + +For example: {cyan}${{info:autosort_order,${{server}},freenode,oftc,efnet}}{reset} will sort freenode before oftc, followed by efnet and then any remaining servers. +Alternatively, {cyan}${{info:autosort_order,${{server}},freenode,oftc,*,efnet}}{reset} will sort any unlisted servers after freenode and oftc, but before efnet. + +{*white}${{info:{brown}autosort_escape{white},{cyan}text{white}}}{reset} +Escape commas and backslashes in {cyan}text{reset} by prepending them with a backslash. +This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. +Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments. + +For example, it can be used to safely pass buffer names to {cyan}${{info:autosort_replace}}{reset} like so: +{cyan}${{info:autosort_replace,##,#,${{info:autosort_escape,${{buffer.name}}}}}}{reset}. + + +{*white}# Description +Autosort is a weechat script to automatically keep your buffers sorted. The sort +order can be customized by defining your own sort rules, but the default should +be sane enough for most people. It can also group IRC channel/private buffers +under their server buffer if you like. + +Autosort uses a stable sorting algorithm, meaning that you can manually move buffers +to change their relative order, if they sort equal with your rule set. + +{*white}# Sort rules{reset} +Autosort evaluates a list of eval expressions (see {*default}/help eval{reset}) and sorts the +buffers based on evaluated result. Earlier rules will be considered first. Only +if earlier rules produced identical results is the result of the next rule +considered for sorting purposes. + +You can debug your sort rules with the `{*default}/autosort debug{reset}` command, which will +print the evaluation results of each rule for each buffer. + +{*brown}NOTE:{reset} The sort rules for version 3 are not compatible with version 2 or vice +versa. You will have to manually port your old rules to version 3 if you have any. + +{*white}# Helper variables{reset} +You may define helper variables for the main sort rules to keep your rules +readable. They can be used in the main sort rules as variables. For example, +a helper variable named `{cyan}foo{reset}` can be accessed in a main rule with the +string `{cyan}${{foo}}{reset}`. + +{*white}# Automatic or manual sorting{reset} +By default, autosort will automatically sort your buffer list whenever a buffer +is opened, merged, unmerged or renamed. This should keep your buffers sorted in +almost all situations. However, you may wish to change the list of signals that +cause your buffer list to be sorted. Simply edit the `{cyan}autosort.sorting.signals{reset}` +option to add or remove any signal you like. + +If you remove all signals you can still sort your buffers manually with the +`{*default}/autosort sort{reset}` command. To prevent all automatic sorting, the option +`{cyan}autosort.sorting.sort_on_config_change{reset}` should also be disabled. + +{*white}# Recommended settings +For the best visual effect, consider setting the following options: + {*white}/set {cyan}irc.look.server_buffer{reset} {brown}independent{reset} + +This setting allows server buffers to be sorted independently, which is +needed to create a hierarchical tree view of the server and channel buffers. + +If you are using the {*default}buflist{reset} plugin you can (ab)use Unicode to draw a tree +structure with the following setting (modify to suit your need): + {*white}/set {cyan}buflist.format.indent {brown}"${{color:237}}${{if:${{buffer.next_buffer.local_variables.type}}=~^(channel|private)$?├─:└─}}"{reset} +''' + +command_completion = '%(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort)' + +info_replace_description = ( + 'Replace all occurrences of `pattern` with `replacement` in the string `source`. ' + 'Can be used to ignore certain strings when sorting by replacing them with an empty string. ' + 'See /help autosort for examples.' +) +info_replace_arguments = 'pattern,replacement,source' + +info_order_description = ( + 'Generate a zero-padded number that corresponds to the index of `value` in the list of options. ' + 'If one of the options is the special value `*`, then any value not explicitly mentioned will be sorted at that position. ' + 'Otherwise, any value that does not match an option is assigned the highest number available. ' + 'Can be used to easily sort buffers based on a manual sequence. ' + 'See /help autosort for examples.' +) +info_order_arguments = 'value,first,second,third,...' + +info_escape_description = ( + 'Escape commas and backslashes in `text` by prepending them with a backslash. ' + 'This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. ' + 'Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.' + 'See /help autosort for examples.' +) +info_escape_arguments = 'text' + + +if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): + config = Config('autosort') + + colors = { + 'default': weechat.color('default'), + 'reset': weechat.color('reset'), + 'black': weechat.color('black'), + 'red': weechat.color('red'), + 'green': weechat.color('green'), + 'brown': weechat.color('brown'), + 'yellow': weechat.color('yellow'), + 'blue': weechat.color('blue'), + 'magenta': weechat.color('magenta'), + 'cyan': weechat.color('cyan'), + 'white': weechat.color('white'), + '*default': weechat.color('*default'), + '*black': weechat.color('*black'), + '*red': weechat.color('*red'), + '*green': weechat.color('*green'), + '*brown': weechat.color('*brown'), + '*yellow': weechat.color('*yellow'), + '*blue': weechat.color('*blue'), + '*magenta': weechat.color('*magenta'), + '*cyan': weechat.color('*cyan'), + '*white': weechat.color('*white'), + } + + weechat.hook_config('autosort.*', 'on_config_changed', '') + weechat.hook_completion('plugin_autosort', '', 'on_autosort_complete', '') + weechat.hook_command('autosort', command_description.format(**colors), '', '', command_completion, 'on_autosort_command', '') + weechat.hook_info('autosort_escape', info_escape_description, info_escape_arguments, 'on_info_escape', '') + weechat.hook_info('autosort_replace', info_replace_description, info_replace_arguments, 'on_info_replace', '') + weechat.hook_info('autosort_order', info_order_description, info_order_arguments, 'on_info_order', '') + + apply_config() diff --git a/config/weechat/python/colorize_nicks.py b/config/weechat/python/colorize_nicks.py new file mode 100644 index 00000000..cb95a0d6 --- /dev/null +++ b/config/weechat/python/colorize_nicks.py @@ -0,0 +1,409 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2010 by xt +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This script colors nicks in IRC channels in the actual message +# not just in the prefix section. +# +# +# History: +# 2020-11-29: jess +# version 28: fix ignore_tags having been broken by weechat 2.9 changes +# 2020-05-09: Sébastien Helleu +# version 27: add compatibility with new weechat_print modifier data +# (WeeChat >= 2.9) +# 2018-04-06: Joey Pabalinas +# version 26: fix freezes with too many nicks in one line +# 2018-03-18: nils_2 +# version 25: fix unable to run function colorize_config_reload_cb() +# 2017-06-20: lbeziaud +# version 24: colorize utf8 nicks +# 2017-03-01, arza +# version 23: don't colorize nicklist group names +# 2016-05-01, Simmo Saan +# version 22: invalidate cached colors on hash algorithm change +# 2015-07-28, xt +# version 21: fix problems with nicks with commas in them +# 2015-04-19, xt +# version 20: fix ignore of nicks in URLs +# 2015-04-18, xt +# version 19: new option ignore nicks in URLs +# 2015-03-03, xt +# version 18: iterate buffers looking for nicklists instead of servers +# 2015-02-23, holomorph +# version 17: fix coloring in non-channel buffers (#58) +# 2014-09-17, holomorph +# version 16: use weechat config facilities +# clean unused, minor linting, some simplification +# 2014-05-05, holomorph +# version 15: fix python2-specific re.search check +# 2013-01-29, nils_2 +# version 14: make script compatible with Python 3.x +# 2012-10-19, ldvx +# version 13: Iterate over every word to prevent incorrect colorization of +# nicks. Added option greedy_matching. +# 2012-04-28, ldvx +# version 12: added ignore_tags to avoid colorizing nicks if tags are present +# 2012-01-14, nesthib +# version 11: input_text_display hook and modifier to colorize nicks in input bar +# 2010-12-22, xt +# version 10: hook config option for updating blacklist +# 2010-12-20, xt +# version 0.9: hook new config option for weechat 0.3.4 +# 2010-11-01, nils_2 +# version 0.8: hook_modifier() added to communicate with rainbow_text +# 2010-10-01, xt +# version 0.7: changes to support non-irc-plugins +# 2010-07-29, xt +# version 0.6: compile regexp as per patch from Chris quigybo@hotmail.com +# 2010-07-19, xt +# version 0.5: fix bug with incorrect coloring of own nick +# 2010-06-02, xt +# version 0.4: update to reflect API changes +# 2010-03-26, xt +# version 0.3: fix error with exception +# 2010-03-24, xt +# version 0.2: use ignore_channels when populating to increase performance. +# 2010-02-03, xt +# version 0.1: initial (based on ruby script by dominikh) +# +# Known issues: nicks will not get colorized if they begin with a character +# such as ~ (which some irc networks do happen to accept) + +import weechat +import re +w = weechat + +SCRIPT_NAME = "colorize_nicks" +SCRIPT_AUTHOR = "xt " +SCRIPT_VERSION = "28" +SCRIPT_LICENSE = "GPL" +SCRIPT_DESC = "Use the weechat nick colors in the chat area" + +# Based on the recommendations in RFC 7613. A valid nick is composed +# of anything but " ,*?.!@". +VALID_NICK = r'([@~&!%+-])?([^\s,\*?\.!@]+)' +valid_nick_re = re.compile(VALID_NICK) +ignore_channels = [] +ignore_nicks = [] + +# Dict with every nick on every channel with its color as lookup value +colored_nicks = {} + +CONFIG_FILE_NAME = "colorize_nicks" + +# config file and options +colorize_config_file = "" +colorize_config_option = {} + +def colorize_config_init(): + ''' + Initialization of configuration file. + Sections: look. + ''' + global colorize_config_file, colorize_config_option + colorize_config_file = weechat.config_new(CONFIG_FILE_NAME, + "", "") + if colorize_config_file == "": + return + + # section "look" + section_look = weechat.config_new_section( + colorize_config_file, "look", 0, 0, "", "", "", "", "", "", "", "", "", "") + if section_look == "": + weechat.config_free(colorize_config_file) + return + colorize_config_option["blacklist_channels"] = weechat.config_new_option( + colorize_config_file, section_look, "blacklist_channels", + "string", "Comma separated list of channels", "", 0, 0, + "", "", 0, "", "", "", "", "", "") + colorize_config_option["blacklist_nicks"] = weechat.config_new_option( + colorize_config_file, section_look, "blacklist_nicks", + "string", "Comma separated list of nicks", "", 0, 0, + "so,root", "so,root", 0, "", "", "", "", "", "") + colorize_config_option["min_nick_length"] = weechat.config_new_option( + colorize_config_file, section_look, "min_nick_length", + "integer", "Minimum length nick to colorize", "", + 2, 20, "", "", 0, "", "", "", "", "", "") + colorize_config_option["colorize_input"] = weechat.config_new_option( + colorize_config_file, section_look, "colorize_input", + "boolean", "Whether to colorize input", "", 0, + 0, "off", "off", 0, "", "", "", "", "", "") + colorize_config_option["ignore_tags"] = weechat.config_new_option( + colorize_config_file, section_look, "ignore_tags", + "string", "Comma separated list of tags to ignore; i.e. irc_join,irc_part,irc_quit", "", 0, 0, + "", "", 0, "", "", "", "", "", "") + colorize_config_option["greedy_matching"] = weechat.config_new_option( + colorize_config_file, section_look, "greedy_matching", + "boolean", "If off, then use lazy matching instead", "", 0, + 0, "on", "on", 0, "", "", "", "", "", "") + colorize_config_option["match_limit"] = weechat.config_new_option( + colorize_config_file, section_look, "match_limit", + "integer", "Fall back to lazy matching if greedy matches exceeds this number", "", + 20, 1000, "", "", 0, "", "", "", "", "", "") + colorize_config_option["ignore_nicks_in_urls"] = weechat.config_new_option( + colorize_config_file, section_look, "ignore_nicks_in_urls", + "boolean", "If on, don't colorize nicks inside URLs", "", 0, + 0, "off", "off", 0, "", "", "", "", "", "") + +def colorize_config_read(): + ''' Read configuration file. ''' + global colorize_config_file + return weechat.config_read(colorize_config_file) + +def colorize_nick_color(nick, my_nick): + ''' Retrieve nick color from weechat. ''' + if nick == my_nick: + return w.color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) + else: + return w.info_get('irc_nick_color', nick) + +def colorize_cb(data, modifier, modifier_data, line): + ''' Callback that does the colorizing, and returns new line if changed ''' + + global ignore_nicks, ignore_channels, colored_nicks + + if modifier_data.startswith('0x'): + # WeeChat >= 2.9 + buffer, tags = modifier_data.split(';', 1) + else: + # WeeChat <= 2.8 + plugin, buffer_name, tags = modifier_data.split(';', 2) + buffer = w.buffer_search(plugin, buffer_name) + + channel = w.buffer_get_string(buffer, 'localvar_channel') + tags = tags.split(',') + + # Check if buffer has colorized nicks + if buffer not in colored_nicks: + return line + + if channel and channel in ignore_channels: + return line + + min_length = w.config_integer(colorize_config_option['min_nick_length']) + reset = w.color('reset') + + # Don't colorize if the ignored tag is present in message + tag_ignores = w.config_string(colorize_config_option['ignore_tags']).split(',') + for tag in tags: + if tag in tag_ignores: + return line + + for words in valid_nick_re.findall(line): + nick = words[1] + # Check that nick is not ignored and longer than minimum length + if len(nick) < min_length or nick in ignore_nicks: + continue + + # If the matched word is not a known nick, we try to match the + # word without its first or last character (if not a letter). + # This is necessary as "foo:" is a valid nick, which could be + # adressed as "foo::". + if nick not in colored_nicks[buffer]: + if not nick[-1].isalpha() and not nick[0].isalpha(): + if nick[1:-1] in colored_nicks[buffer]: + nick = nick[1:-1] + elif not nick[0].isalpha(): + if nick[1:] in colored_nicks[buffer]: + nick = nick[1:] + elif not nick[-1].isalpha(): + if nick[:-1] in colored_nicks[buffer]: + nick = nick[:-1] + + # Check that nick is in the dictionary colored_nicks + if nick in colored_nicks[buffer]: + nick_color = colored_nicks[buffer][nick] + + try: + # Let's use greedy matching. Will check against every word in a line. + if w.config_boolean(colorize_config_option['greedy_matching']): + cnt = 0 + limit = w.config_integer(colorize_config_option['match_limit']) + + for word in line.split(): + cnt += 1 + assert cnt < limit + # if cnt > limit: + # raise RuntimeError('Exceeded colorize_nicks.look.match_limit.'); + + if w.config_boolean(colorize_config_option['ignore_nicks_in_urls']) and \ + word.startswith(('http://', 'https://')): + continue + + if nick in word: + # Is there a nick that contains nick and has a greater lenght? + # If so let's save that nick into var biggest_nick + biggest_nick = "" + for i in colored_nicks[buffer]: + cnt += 1 + assert cnt < limit + + if nick in i and nick != i and len(i) > len(nick): + if i in word: + # If a nick with greater len is found, and that word + # also happens to be in word, then let's save this nick + biggest_nick = i + # If there's a nick with greater len, then let's skip this + # As we will have the chance to colorize when biggest_nick + # iterates being nick. + if len(biggest_nick) > 0 and biggest_nick in word: + pass + elif len(word) < len(biggest_nick) or len(biggest_nick) == 0: + new_word = word.replace(nick, '%s%s%s' % (nick_color, nick, reset)) + line = line.replace(word, new_word) + + # Switch to lazy matching + else: + raise AssertionError + + except AssertionError: + # Let's use lazy matching for nick + nick_color = colored_nicks[buffer][nick] + # The two .? are in case somebody writes "nick:", "nick,", etc + # to address somebody + regex = r"(\A|\s).?(%s).?(\Z|\s)" % re.escape(nick) + match = re.search(regex, line) + if match is not None: + new_line = line[:match.start(2)] + nick_color+nick+reset + line[match.end(2):] + line = new_line + + return line + +def colorize_input_cb(data, modifier, modifier_data, line): + ''' Callback that does the colorizing in input ''' + + global ignore_nicks, ignore_channels, colored_nicks + + min_length = w.config_integer(colorize_config_option['min_nick_length']) + + if not w.config_boolean(colorize_config_option['colorize_input']): + return line + + buffer = w.current_buffer() + # Check if buffer has colorized nicks + if buffer not in colored_nicks: + return line + + channel = w.buffer_get_string(buffer, 'name') + if channel and channel in ignore_channels: + return line + + reset = w.color('reset') + + for words in valid_nick_re.findall(line): + nick = words[1] + # Check that nick is not ignored and longer than minimum length + if len(nick) < min_length or nick in ignore_nicks: + continue + if nick in colored_nicks[buffer]: + nick_color = colored_nicks[buffer][nick] + line = line.replace(nick, '%s%s%s' % (nick_color, nick, reset)) + + return line + +def populate_nicks(*args): + ''' Fills entire dict with all nicks weechat can see and what color it has + assigned to it. ''' + global colored_nicks + + colored_nicks = {} + + buffers = w.infolist_get('buffer', '', '') + while w.infolist_next(buffers): + buffer_ptr = w.infolist_pointer(buffers, 'pointer') + my_nick = w.buffer_get_string(buffer_ptr, 'localvar_nick') + nicklist = w.infolist_get('nicklist', buffer_ptr, '') + while w.infolist_next(nicklist): + if buffer_ptr not in colored_nicks: + colored_nicks[buffer_ptr] = {} + + if w.infolist_string(nicklist, 'type') != 'nick': + continue + + nick = w.infolist_string(nicklist, 'name') + nick_color = colorize_nick_color(nick, my_nick) + + colored_nicks[buffer_ptr][nick] = nick_color + + w.infolist_free(nicklist) + + w.infolist_free(buffers) + + return w.WEECHAT_RC_OK + +def add_nick(data, signal, type_data): + ''' Add nick to dict of colored nicks ''' + global colored_nicks + + # Nicks can have , in them in some protocols + splitted = type_data.split(',') + pointer = splitted[0] + nick = ",".join(splitted[1:]) + if pointer not in colored_nicks: + colored_nicks[pointer] = {} + + my_nick = w.buffer_get_string(pointer, 'localvar_nick') + nick_color = colorize_nick_color(nick, my_nick) + + colored_nicks[pointer][nick] = nick_color + + return w.WEECHAT_RC_OK + +def remove_nick(data, signal, type_data): + ''' Remove nick from dict with colored nicks ''' + global colored_nicks + + # Nicks can have , in them in some protocols + splitted = type_data.split(',') + pointer = splitted[0] + nick = ",".join(splitted[1:]) + + if pointer in colored_nicks and nick in colored_nicks[pointer]: + del colored_nicks[pointer][nick] + + return w.WEECHAT_RC_OK + +def update_blacklist(*args): + ''' Set the blacklist for channels and nicks. ''' + global ignore_channels, ignore_nicks + ignore_channels = w.config_string(colorize_config_option['blacklist_channels']).split(',') + ignore_nicks = w.config_string(colorize_config_option['blacklist_nicks']).split(',') + return w.WEECHAT_RC_OK + +if __name__ == "__main__": + if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, + SCRIPT_DESC, "", ""): + colorize_config_init() + colorize_config_read() + + # Run once to get data ready + update_blacklist() + populate_nicks() + + w.hook_signal('nicklist_nick_added', 'add_nick', '') + w.hook_signal('nicklist_nick_removed', 'remove_nick', '') + w.hook_modifier('weechat_print', 'colorize_cb', '') + # Hook config for changing colors + w.hook_config('weechat.color.chat_nick_colors', 'populate_nicks', '') + w.hook_config('weechat.look.nick_color_hash', 'populate_nicks', '') + # Hook for working togheter with other scripts (like colorize_lines) + w.hook_modifier('colorize_nicks', 'colorize_cb', '') + # Hook for modifying input + w.hook_modifier('250|input_text_display', 'colorize_input_cb', '') + # Hook for updating blacklist (this could be improved to use fnmatch) + weechat.hook_config('%s.look.blacklist*' % SCRIPT_NAME, 'update_blacklist', '') diff --git a/config/weechat/python/go.py b/config/weechat/python/go.py new file mode 100644 index 00000000..2ab47ed4 --- /dev/null +++ b/config/weechat/python/go.py @@ -0,0 +1,563 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009-2014 Sébastien Helleu +# Copyright (C) 2010 m4v +# Copyright (C) 2011 stfn +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# +# History: +# +# 2019-07-11, Simmo Saan +# version 2.6: fix detection of "/input search_text_here" +# 2017-04-01, Sébastien Helleu : +# version 2.5: add option "buffer_number" +# 2017-03-02, Sébastien Helleu : +# version 2.4: fix syntax and indentation error +# 2017-02-25, Simmo Saan +# version 2.3: fix fuzzy search breaking buffer number search display +# 2016-01-28, ylambda +# version 2.2: add option "fuzzy_search" +# 2015-11-12, nils_2 +# version 2.1: fix problem with buffer short_name "weechat", using option +# "use_core_instead_weechat", see: +# https://github.com/weechat/weechat/issues/574 +# 2014-05-12, Sébastien Helleu : +# version 2.0: add help on options, replace option "sort_by_activity" by +# "sort" (add sort by name and first match at beginning of +# name and by number), PEP8 compliance +# 2012-11-26, Nei +# version 1.9: add auto_jump option to automatically go to buffer when it +# is uniquely selected +# 2012-09-17, Sébastien Helleu : +# version 1.8: fix jump to non-active merged buffers (jump with buffer name +# instead of number) +# 2012-01-03 nils_2 +# version 1.7: add option "use_core_instead_weechat" +# 2012-01-03, Sébastien Helleu : +# version 1.6: make script compatible with Python 3.x +# 2011-08-24, stfn : +# version 1.5: /go with name argument jumps directly to buffer +# Remember cursor position in buffer input +# 2011-05-31, Elián Hanisch : +# version 1.4: Sort list of buffers by activity. +# 2011-04-25, Sébastien Helleu : +# version 1.3: add info "go_running" (used by script input_lock.rb) +# 2010-11-01, Sébastien Helleu : +# version 1.2: use high priority for hooks to prevent conflict with other +# plugins/scripts (WeeChat >= 0.3.4 only) +# 2010-03-25, Elián Hanisch : +# version 1.1: use a space to match the end of a string +# 2009-11-16, Sébastien Helleu : +# version 1.0: add new option to display short names +# 2009-06-15, Sébastien Helleu : +# version 0.9: fix typo in /help go with command /key +# 2009-05-16, Sébastien Helleu : +# version 0.8: search buffer by number, fix bug when window is split +# 2009-05-03, Sébastien Helleu : +# version 0.7: eat tab key (do not complete input, just move buffer +# pointer) +# 2009-05-02, Sébastien Helleu : +# version 0.6: sync with last API changes +# 2009-03-22, Sébastien Helleu : +# version 0.5: update modifier signal name for input text display, +# fix arguments for function string_remove_color +# 2009-02-18, Sébastien Helleu : +# version 0.4: do not hook command and init options if register failed +# 2009-02-08, Sébastien Helleu : +# version 0.3: case insensitive search for buffers names +# 2009-02-08, Sébastien Helleu : +# version 0.2: add help about Tab key +# 2009-02-08, Sébastien Helleu : +# version 0.1: initial release +# + +""" +Quick jump to buffers. +(this script requires WeeChat 0.3.0 or newer) +""" + +from __future__ import print_function + +SCRIPT_NAME = 'go' +SCRIPT_AUTHOR = 'Sébastien Helleu ' +SCRIPT_VERSION = '2.6' +SCRIPT_LICENSE = 'GPL3' +SCRIPT_DESC = 'Quick jump to buffers' + +SCRIPT_COMMAND = 'go' + +IMPORT_OK = True + +try: + import weechat +except ImportError: + print('This script must be run under WeeChat.') + print('Get WeeChat now at: http://www.weechat.org/') + IMPORT_OK = False + +import re + +# script options +SETTINGS = { + 'color_number': ( + 'yellow,magenta', + 'color for buffer number (not selected)'), + 'color_number_selected': ( + 'yellow,red', + 'color for selected buffer number'), + 'color_name': ( + 'black,cyan', + 'color for buffer name (not selected)'), + 'color_name_selected': ( + 'black,brown', + 'color for a selected buffer name'), + 'color_name_highlight': ( + 'red,cyan', + 'color for highlight in buffer name (not selected)'), + 'color_name_highlight_selected': ( + 'red,brown', + 'color for highlight in a selected buffer name'), + 'message': ( + 'Go to: ', + 'message to display before list of buffers'), + 'short_name': ( + 'off', + 'display and search in short names instead of buffer name'), + 'sort': ( + 'number,beginning', + 'comma-separated list of keys to sort buffers ' + '(the order is important, sorts are performed in the given order): ' + 'name = sort by name (or short name), ', + 'hotlist = sort by hotlist order, ' + 'number = first match a buffer number before digits in name, ' + 'beginning = first match at beginning of names (or short names); ' + 'the default sort of buffers is by numbers'), + 'use_core_instead_weechat': ( + 'off', + 'use name "core" instead of "weechat" for core buffer'), + 'auto_jump': ( + 'off', + 'automatically jump to buffer when it is uniquely selected'), + 'fuzzy_search': ( + 'off', + 'search buffer matches using approximation'), + 'buffer_number': ( + 'on', + 'display buffer number'), +} + +# hooks management +HOOK_COMMAND_RUN = { + 'input': ('/input *', 'go_command_run_input'), + 'buffer': ('/buffer *', 'go_command_run_buffer'), + 'window': ('/window *', 'go_command_run_window'), +} +hooks = {} + +# input before command /go (we'll restore it later) +saved_input = '' +saved_input_pos = 0 + +# last user input (if changed, we'll update list of matching buffers) +old_input = None + +# matching buffers +buffers = [] +buffers_pos = 0 + + +def go_option_enabled(option): + """Checks if a boolean script option is enabled or not.""" + return weechat.config_string_to_boolean(weechat.config_get_plugin(option)) + + +def go_info_running(data, info_name, arguments): + """Returns "1" if go is running, otherwise "0".""" + return '1' if 'modifier' in hooks else '0' + + +def go_unhook_one(hook): + """Unhook something hooked by this script.""" + global hooks + if hook in hooks: + weechat.unhook(hooks[hook]) + del hooks[hook] + + +def go_unhook_all(): + """Unhook all.""" + go_unhook_one('modifier') + for hook in HOOK_COMMAND_RUN: + go_unhook_one(hook) + + +def go_hook_all(): + """Hook command_run and modifier.""" + global hooks + priority = '' + version = weechat.info_get('version_number', '') or 0 + # use high priority for hook to prevent conflict with other plugins/scripts + # (WeeChat >= 0.3.4 only) + if int(version) >= 0x00030400: + priority = '2000|' + for hook, value in HOOK_COMMAND_RUN.items(): + if hook not in hooks: + hooks[hook] = weechat.hook_command_run( + '%s%s' % (priority, value[0]), + value[1], '') + if 'modifier' not in hooks: + hooks['modifier'] = weechat.hook_modifier( + 'input_text_display_with_cursor', 'go_input_modifier', '') + + +def go_start(buf): + """Start go on buffer.""" + global saved_input, saved_input_pos, old_input, buffers_pos + go_hook_all() + saved_input = weechat.buffer_get_string(buf, 'input') + saved_input_pos = weechat.buffer_get_integer(buf, 'input_pos') + weechat.buffer_set(buf, 'input', '') + old_input = None + buffers_pos = 0 + + +def go_end(buf): + """End go on buffer.""" + global saved_input, saved_input_pos, old_input + go_unhook_all() + weechat.buffer_set(buf, 'input', saved_input) + weechat.buffer_set(buf, 'input_pos', str(saved_input_pos)) + old_input = None + + +def go_match_beginning(buf, string): + """Check if a string matches the beginning of buffer name/short name.""" + if not string: + return False + esc_str = re.escape(string) + if re.search(r'^#?' + esc_str, buf['name']) \ + or re.search(r'^#?' + esc_str, buf['short_name']): + return True + return False + + +def go_match_fuzzy(name, string): + """Check if string matches name using approximation.""" + if not string: + return False + + name_len = len(name) + string_len = len(string) + + if string_len > name_len: + return False + if name_len == string_len: + return name == string + + # Attempt to match all chars somewhere in name + prev_index = -1 + for i, char in enumerate(string): + index = name.find(char, prev_index+1) + if index == -1: + return False + prev_index = index + return True + + +def go_now(buf, args): + """Go to buffer specified by args.""" + listbuf = go_matching_buffers(args) + if not listbuf: + return + + # prefer buffer that matches at beginning (if option is enabled) + if 'beginning' in weechat.config_get_plugin('sort').split(','): + for index in range(len(listbuf)): + if go_match_beginning(listbuf[index], args): + weechat.command(buf, + '/buffer ' + str(listbuf[index]['full_name'])) + return + + # jump to first buffer in matching buffers by default + weechat.command(buf, '/buffer ' + str(listbuf[0]['full_name'])) + + +def go_cmd(data, buf, args): + """Command "/go": just hook what we need.""" + global hooks + if args: + go_now(buf, args) + elif 'modifier' in hooks: + go_end(buf) + else: + go_start(buf) + return weechat.WEECHAT_RC_OK + + +def go_matching_buffers(strinput): + """Return a list with buffers matching user input.""" + global buffers_pos + listbuf = [] + if len(strinput) == 0: + buffers_pos = 0 + strinput = strinput.lower() + infolist = weechat.infolist_get('buffer', '', '') + while weechat.infolist_next(infolist): + short_name = weechat.infolist_string(infolist, 'short_name') + if go_option_enabled('short_name'): + name = weechat.infolist_string(infolist, 'short_name') + else: + name = weechat.infolist_string(infolist, 'name') + if name == 'weechat' \ + and go_option_enabled('use_core_instead_weechat') \ + and weechat.infolist_string(infolist, 'plugin_name') == 'core': + name = 'core' + number = weechat.infolist_integer(infolist, 'number') + full_name = weechat.infolist_string(infolist, 'full_name') + if not full_name: + full_name = '%s.%s' % ( + weechat.infolist_string(infolist, 'plugin_name'), + weechat.infolist_string(infolist, 'name')) + pointer = weechat.infolist_pointer(infolist, 'pointer') + matching = name.lower().find(strinput) >= 0 + if not matching and strinput[-1] == ' ': + matching = name.lower().endswith(strinput.strip()) + if not matching and go_option_enabled('fuzzy_search'): + matching = go_match_fuzzy(name.lower(), strinput) + if not matching and strinput.isdigit(): + matching = str(number).startswith(strinput) + if len(strinput) == 0 or matching: + listbuf.append({ + 'number': number, + 'short_name': short_name, + 'name': name, + 'full_name': full_name, + 'pointer': pointer, + }) + weechat.infolist_free(infolist) + + # sort buffers + hotlist = [] + infolist = weechat.infolist_get('hotlist', '', '') + while weechat.infolist_next(infolist): + hotlist.append( + weechat.infolist_pointer(infolist, 'buffer_pointer')) + weechat.infolist_free(infolist) + last_index_hotlist = len(hotlist) + + def _sort_name(buf): + """Sort buffers by name (or short name).""" + return buf['name'] + + def _sort_hotlist(buf): + """Sort buffers by hotlist order.""" + try: + return hotlist.index(buf['pointer']) + except ValueError: + # not in hotlist, always last. + return last_index_hotlist + + def _sort_match_number(buf): + """Sort buffers by match on number.""" + return 0 if str(buf['number']) == strinput else 1 + + def _sort_match_beginning(buf): + """Sort buffers by match at beginning.""" + return 0 if go_match_beginning(buf, strinput) else 1 + + funcs = { + 'name': _sort_name, + 'hotlist': _sort_hotlist, + 'number': _sort_match_number, + 'beginning': _sort_match_beginning, + } + + for key in weechat.config_get_plugin('sort').split(','): + if key in funcs: + listbuf = sorted(listbuf, key=funcs[key]) + + if not strinput: + index = [i for i, buf in enumerate(listbuf) + if buf['pointer'] == weechat.current_buffer()] + if index: + buffers_pos = index[0] + + return listbuf + + +def go_buffers_to_string(listbuf, pos, strinput): + """Return string built with list of buffers found (matching user input).""" + string = '' + strinput = strinput.lower() + for i in range(len(listbuf)): + selected = '_selected' if i == pos else '' + buffer_name = listbuf[i]['name'] + index = buffer_name.lower().find(strinput) + if index >= 0: + index2 = index + len(strinput) + name = '%s%s%s%s%s' % ( + buffer_name[:index], + weechat.color(weechat.config_get_plugin( + 'color_name_highlight' + selected)), + buffer_name[index:index2], + weechat.color(weechat.config_get_plugin( + 'color_name' + selected)), + buffer_name[index2:]) + elif go_option_enabled("fuzzy_search") and \ + go_match_fuzzy(buffer_name.lower(), strinput): + name = "" + prev_index = -1 + for char in strinput.lower(): + index = buffer_name.lower().find(char, prev_index+1) + if prev_index < 0: + name += buffer_name[:index] + name += weechat.color(weechat.config_get_plugin( + 'color_name_highlight' + selected)) + if prev_index >= 0 and index > prev_index+1: + name += weechat.color(weechat.config_get_plugin( + 'color_name' + selected)) + name += buffer_name[prev_index+1:index] + name += weechat.color(weechat.config_get_plugin( + 'color_name_highlight' + selected)) + name += buffer_name[index] + prev_index = index + + name += weechat.color(weechat.config_get_plugin( + 'color_name' + selected)) + name += buffer_name[prev_index+1:] + else: + name = buffer_name + string += ' ' + if go_option_enabled('buffer_number'): + string += '%s%s' % ( + weechat.color(weechat.config_get_plugin( + 'color_number' + selected)), + str(listbuf[i]['number'])) + string += '%s%s%s' % ( + weechat.color(weechat.config_get_plugin( + 'color_name' + selected)), + name, + weechat.color('reset')) + return ' ' + string if string else '' + + +def go_input_modifier(data, modifier, modifier_data, string): + """This modifier is called when input text item is built by WeeChat. + + This is commonly called after changes in input or cursor move: it builds + a new input with prefix ("Go to:"), and suffix (list of buffers found). + """ + global old_input, buffers, buffers_pos + if modifier_data != weechat.current_buffer(): + return '' + names = '' + new_input = weechat.string_remove_color(string, '') + new_input = new_input.lstrip() + if old_input is None or new_input != old_input: + old_buffers = buffers + buffers = go_matching_buffers(new_input) + if buffers != old_buffers and len(new_input) > 0: + if len(buffers) == 1 and go_option_enabled('auto_jump'): + weechat.command(modifier_data, '/wait 1ms /input return') + buffers_pos = 0 + old_input = new_input + names = go_buffers_to_string(buffers, buffers_pos, new_input.strip()) + return weechat.config_get_plugin('message') + string + names + + +def go_command_run_input(data, buf, command): + """Function called when a command "/input xxx" is run.""" + global buffers, buffers_pos + if command.startswith('/input search_text') or command.startswith('/input jump'): + # search text or jump to another buffer is forbidden now + return weechat.WEECHAT_RC_OK_EAT + elif command == '/input complete_next': + # choose next buffer in list + buffers_pos += 1 + if buffers_pos >= len(buffers): + buffers_pos = 0 + weechat.hook_signal_send('input_text_changed', + weechat.WEECHAT_HOOK_SIGNAL_STRING, '') + return weechat.WEECHAT_RC_OK_EAT + elif command == '/input complete_previous': + # choose previous buffer in list + buffers_pos -= 1 + if buffers_pos < 0: + buffers_pos = len(buffers) - 1 + weechat.hook_signal_send('input_text_changed', + weechat.WEECHAT_HOOK_SIGNAL_STRING, '') + return weechat.WEECHAT_RC_OK_EAT + elif command == '/input return': + # switch to selected buffer (if any) + go_end(buf) + if len(buffers) > 0: + weechat.command( + buf, '/buffer ' + str(buffers[buffers_pos]['full_name'])) + return weechat.WEECHAT_RC_OK_EAT + return weechat.WEECHAT_RC_OK + + +def go_command_run_buffer(data, buf, command): + """Function called when a command "/buffer xxx" is run.""" + return weechat.WEECHAT_RC_OK_EAT + + +def go_command_run_window(data, buf, command): + """Function called when a command "/window xxx" is run.""" + return weechat.WEECHAT_RC_OK_EAT + + +def go_unload_script(): + """Function called when script is unloaded.""" + go_unhook_all() + return weechat.WEECHAT_RC_OK + + +def go_main(): + """Entry point.""" + if not weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, + SCRIPT_LICENSE, SCRIPT_DESC, + 'go_unload_script', ''): + return + weechat.hook_command( + SCRIPT_COMMAND, + 'Quick jump to buffers', '[name]', + 'name: directly jump to buffer by name (without argument, list is ' + 'displayed)\n\n' + 'You can bind command to a key, for example:\n' + ' /key bind meta-g /go\n\n' + 'You can use completion key (commonly Tab and shift-Tab) to select ' + 'next/previous buffer in list.', + '%(buffers_names)', + 'go_cmd', '') + + # set default settings + version = weechat.info_get('version_number', '') or 0 + for option, value in SETTINGS.items(): + if not weechat.config_is_set_plugin(option): + weechat.config_set_plugin(option, value[0]) + if int(version) >= 0x00030500: + weechat.config_set_desc_plugin( + option, '%s (default: "%s")' % (value[1], value[0])) + weechat.hook_info('go_running', + 'Return "1" if go is running, otherwise "0"', + '', + 'go_info_running', '') + + +if __name__ == "__main__" and IMPORT_OK: + go_main() diff --git a/config/weechat/relay.conf b/config/weechat/relay.conf new file mode 100644 index 00000000..15346c33 --- /dev/null +++ b/config/weechat/relay.conf @@ -0,0 +1,59 @@ +# +# weechat -- relay.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +auto_open_buffer = on +raw_messages = 256 + +[color] +client = cyan +status_active = green +status_auth_failed = lightmagenta +status_connecting = white +status_disconnected = lightred +status_waiting_auth = yellow +text = default +text_bg = default +text_selected = white + +[network] +allow_empty_password = off +allowed_ips = "" +auth_timeout = 60 +bind_address = "" +clients_purge_delay = 0 +compression_level = 6 +ipv6 = on +max_clients = 5 +nonce_size = 16 +password = "" +password_hash_algo = "*" +password_hash_iterations = 100000 +ssl_cert_key = "%h/ssl/relay.pem" +ssl_priorities = "NORMAL:-VERS-SSL3.0" +totp_secret = "" +totp_window = 0 +websocket_allowed_origins = "" + +[irc] +backlog_max_minutes = 0 +backlog_max_number = 1024 +backlog_since_last_disconnect = on +backlog_since_last_message = off +backlog_tags = "irc_privmsg" +backlog_time_format = "[%H:%M] " + +[weechat] +commands = "" + +[port] + +[path] diff --git a/config/weechat/ruby.conf b/config/weechat/ruby.conf new file mode 100644 index 00000000..f6de7fbf --- /dev/null +++ b/config/weechat/ruby.conf @@ -0,0 +1,14 @@ +# +# weechat -- ruby.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +check_license = off +eval_keep_context = on diff --git a/config/weechat/script.conf b/config/weechat/script.conf new file mode 100644 index 00000000..519bb1d1 --- /dev/null +++ b/config/weechat/script.conf @@ -0,0 +1,57 @@ +# +# weechat -- script.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +columns = "%s %n %V %v %u | %d | %t" +diff_color = on +diff_command = "auto" +display_source = on +quiet_actions = on +sort = "i,p,n" +translate_description = on +use_keys = on + +[color] +status_autoloaded = cyan +status_held = white +status_installed = lightcyan +status_obsolete = lightmagenta +status_popular = yellow +status_running = lightgreen +status_unknown = lightred +text = default +text_bg = default +text_bg_selected = red +text_date = default +text_date_selected = white +text_delimiters = default +text_description = default +text_description_selected = white +text_extension = default +text_extension_selected = white +text_name = cyan +text_name_selected = lightcyan +text_selected = white +text_tags = brown +text_tags_selected = yellow +text_version = magenta +text_version_loaded = default +text_version_loaded_selected = white +text_version_selected = lightmagenta + +[scripts] +autoload = on +cache_expire = 1440 +download_enabled = on +download_timeout = 30 +hold = "" +path = "%h/script" +url = "https://weechat.org/files/plugins.xml.gz" diff --git a/config/weechat/script/plugins.xml.gz b/config/weechat/script/plugins.xml.gz new file mode 100644 index 00000000..a271e7e3 Binary files /dev/null and b/config/weechat/script/plugins.xml.gz differ diff --git a/config/weechat/sec.conf b/config/weechat/sec.conf new file mode 100644 index 00000000..c596b484 --- /dev/null +++ b/config/weechat/sec.conf @@ -0,0 +1,20 @@ +# +# weechat -- sec.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[crypt] +cipher = aes256 +hash_algo = sha256 +passphrase_command = "" +salt = on + +[data] +__passphrase__ = on +freenode_password = "F5FA53FD70C3D1125F2CF4C43A6C450931280E2CA4B9327C7388E1C3AD9A2E45A42C9696703F82874CDA7092F1D3FD362A6622A447702B435E000F" diff --git a/config/weechat/spell.conf b/config/weechat/spell.conf new file mode 100644 index 00000000..f65348de --- /dev/null +++ b/config/weechat/spell.conf @@ -0,0 +1,33 @@ +# +# weechat -- spell.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[color] +misspelled = lightred +suggestion = default +suggestion_delimiter_dict = cyan +suggestion_delimiter_word = cyan + +[check] +commands = "away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic" +default_dict = "" +during_search = off +enabled = off +real_time = off +suggestions = -1 +word_min_length = 2 + +[dict] + +[look] +suggestion_delimiter_dict = " / " +suggestion_delimiter_word = "," + +[option] diff --git a/config/weechat/tcl.conf b/config/weechat/tcl.conf new file mode 100644 index 00000000..4c0e3896 --- /dev/null +++ b/config/weechat/tcl.conf @@ -0,0 +1,14 @@ +# +# weechat -- tcl.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +check_license = off +eval_keep_context = on diff --git a/config/weechat/trigger.conf b/config/weechat/trigger.conf new file mode 100644 index 00000000..b5977ba0 --- /dev/null +++ b/config/weechat/trigger.conf @@ -0,0 +1,67 @@ +# +# weechat -- trigger.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +enabled = on +monitor_strip_colors = off + +[color] +flag_command = lightgreen +flag_conditions = yellow +flag_post_action = lightblue +flag_regex = lightcyan +flag_return_code = lightmagenta +regex = white +replace = cyan +trigger = green +trigger_disabled = red + +[trigger] +beep.arguments = "" +beep.command = "/print -beep" +beep.conditions = "${tg_displayed} && ${tg_tags} !!- ,notify_none, && (${tg_highlight} || ${tg_msg_pv})" +beep.enabled = on +beep.hook = print +beep.post_action = none +beep.regex = "" +beep.return_code = ok +cmd_pass.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth" +cmd_pass.command = "" +cmd_pass.conditions = "" +cmd_pass.enabled = on +cmd_pass.hook = modifier +cmd_pass.post_action = none +cmd_pass.regex = "==^((/(msg|m|quote) +(-server +[^ ]+ +)?nickserv +(id|identify|set +password|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+|recover +[^ ]+) +)|/oper +[^ ]+ +|/quote +pass +|/secure +(passphrase|decrypt|set +[^ ]+) +)(.*)==${re:1}${hide:*,${re:+}}" +cmd_pass.return_code = ok +cmd_pass_register.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth" +cmd_pass_register.command = "" +cmd_pass_register.conditions = "" +cmd_pass_register.enabled = on +cmd_pass_register.hook = modifier +cmd_pass_register.post_action = none +cmd_pass_register.regex = "==^(/(msg|m|quote) +nickserv +register +)([^ ]+)(.*)==${re:1}${hide:*,${re:3}}${re:4}" +cmd_pass_register.return_code = ok +msg_auth.arguments = "5000|irc_message_auth" +msg_auth.command = "" +msg_auth.conditions = "" +msg_auth.enabled = on +msg_auth.hook = modifier +msg_auth.post_action = none +msg_auth.regex = "==^(.*(id|identify|set +password|register|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+|recover +[^ ]+) +)(.*)==${re:1}${hide:*,${re:+}}" +msg_auth.return_code = ok +server_pass.arguments = "5000|input_text_display;5000|history_add" +server_pass.command = "" +server_pass.conditions = "" +server_pass.enabled = on +server_pass.hook = modifier +server_pass.post_action = none +server_pass.regex = "==^(/(server|connect) .*-(sasl_)?password=)([^ ]+)(.*)==${re:1}${hide:*,${re:4}}${re:5}" +server_pass.return_code = ok diff --git a/config/weechat/weechat.conf b/config/weechat/weechat.conf new file mode 100644 index 00000000..2b886ff5 --- /dev/null +++ b/config/weechat/weechat.conf @@ -0,0 +1,679 @@ +# +# weechat -- weechat.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[debug] + +[startup] +command_after_plugins = "" +command_before_plugins = "" +display_logo = on +display_version = on +sys_rlimit = "" + +[look] +align_end_of_lines = message +align_multiline_words = on +bar_more_down = "++" +bar_more_left = "<<" +bar_more_right = ">>" +bar_more_up = "--" +bare_display_exit_on_input = on +bare_display_time_format = "%H:%M" +buffer_auto_renumber = on +buffer_notify_default = all +buffer_position = end +buffer_search_case_sensitive = off +buffer_search_force_default = off +buffer_search_regex = off +buffer_search_where = prefix_message +buffer_time_format = "%H:%M:%S" +buffer_time_same = "" +color_basic_force_bold = off +color_inactive_buffer = on +color_inactive_message = on +color_inactive_prefix = on +color_inactive_prefix_buffer = on +color_inactive_time = off +color_inactive_window = on +color_nick_offline = off +color_pairs_auto_reset = 5 +color_real_white = off +command_chars = "" +command_incomplete = off +confirm_quit = off +confirm_upgrade = off +day_change = on +day_change_message_1date = "-- %a, %d %b %Y --" +day_change_message_2dates = "-- %%a, %%d %%b %%Y (%a, %d %b %Y) --" +eat_newline_glitch = off +emphasized_attributes = "" +highlight = "" +highlight_regex = "" +highlight_tags = "" +hotlist_add_conditions = "${away} || ${buffer.num_displayed} == 0 || ${info:relay_client_count,weechat,connected} > 0" +hotlist_buffer_separator = ", " +hotlist_count_max = 2 +hotlist_count_min_msg = 2 +hotlist_names_count = 3 +hotlist_names_length = 0 +hotlist_names_level = 12 +hotlist_names_merged_buffers = off +hotlist_prefix = "H: " +hotlist_remove = merged +hotlist_short_names = on +hotlist_sort = group_time_asc +hotlist_suffix = "" +hotlist_unique_numbers = on +hotlist_update_on_buffer_switch = on +input_cursor_scroll = 20 +input_share = none +input_share_overwrite = off +input_undo_max = 32 +item_away_message = on +item_buffer_filter = "*" +item_buffer_zoom = "!" +item_mouse_status = "M" +item_time_format = "%H:%M" +jump_current_to_previous_buffer = on +jump_previous_buffer_when_closing = on +jump_smart_back_to_buffer = on +key_bind_safe = on +key_grab_delay = 800 +mouse = off +mouse_timer_delay = 100 +nick_color_force = "" +nick_color_hash = djb2 +nick_color_hash_salt = "" +nick_color_stop_chars = "_|[" +nick_prefix = "" +nick_suffix = "" +paste_auto_add_newline = on +paste_bracketed = on +paste_bracketed_timer_delay = 10 +paste_max_lines = 1 +prefix_action = " *" +prefix_align = right +prefix_align_max = 0 +prefix_align_min = 0 +prefix_align_more = "+" +prefix_align_more_after = on +prefix_buffer_align = right +prefix_buffer_align_max = 0 +prefix_buffer_align_more = "+" +prefix_buffer_align_more_after = on +prefix_error = "=!=" +prefix_join = "-->" +prefix_network = "--" +prefix_quit = "<--" +prefix_same_nick = "" +prefix_same_nick_middle = "" +prefix_suffix = "|" +quote_nick_prefix = "<" +quote_nick_suffix = ">" +quote_time_format = "%H:%M:%S" +read_marker = line +read_marker_always_show = off +read_marker_string = "- " +read_marker_update_on_buffer_switch = on +save_config_on_exit = on +save_config_with_fsync = off +save_layout_on_exit = none +scroll_amount = 3 +scroll_bottom_after_switch = off +scroll_page_percent = 100 +search_text_not_found_alert = on +separator_horizontal = "-" +separator_vertical = "" +tab_width = 1 +time_format = "%a, %d %b %Y %T" +window_auto_zoom = off +window_separator_horizontal = on +window_separator_vertical = on +window_title = "" +word_chars_highlight = "!\u00A0,-,_,|,alnum" +word_chars_input = "!\u00A0,-,_,|,alnum" + +[palette] + +[color] +bar_more = lightmagenta +chat = default +chat_bg = default +chat_buffer = white +chat_channel = white +chat_day_change = cyan +chat_delimiters = green +chat_highlight = yellow +chat_highlight_bg = magenta +chat_host = cyan +chat_inactive_buffer = default +chat_inactive_window = default +chat_nick = lightcyan +chat_nick_colors = "cyan,magenta,green,brown,lightblue,default,lightcyan,lightmagenta,lightgreen,blue" +chat_nick_offline = default +chat_nick_offline_highlight = default +chat_nick_offline_highlight_bg = blue +chat_nick_other = cyan +chat_nick_prefix = green +chat_nick_self = white +chat_nick_suffix = green +chat_prefix_action = white +chat_prefix_buffer = brown +chat_prefix_buffer_inactive_buffer = default +chat_prefix_error = yellow +chat_prefix_join = lightgreen +chat_prefix_more = lightmagenta +chat_prefix_network = magenta +chat_prefix_quit = lightred +chat_prefix_suffix = green +chat_read_marker = magenta +chat_read_marker_bg = default +chat_server = brown +chat_tags = red +chat_text_found = yellow +chat_text_found_bg = lightmagenta +chat_time = default +chat_time_delimiters = brown +chat_value = cyan +chat_value_null = blue +emphasized = yellow +emphasized_bg = magenta +input_actions = lightgreen +input_text_not_found = red +item_away = yellow +nicklist_away = cyan +nicklist_group = green +separator = blue +status_count_highlight = magenta +status_count_msg = brown +status_count_other = default +status_count_private = green +status_data_highlight = lightmagenta +status_data_msg = yellow +status_data_other = default +status_data_private = lightgreen +status_filter = green +status_more = yellow +status_mouse = green +status_name = white +status_name_ssl = lightgreen +status_nicklist_count = default +status_number = yellow +status_time = default + +[completion] +base_word_until_cursor = on +command_inline = on +default_template = "%(nicks)|%(irc_channels)" +nick_add_space = on +nick_case_sensitive = off +nick_completer = ": " +nick_first_only = off +nick_ignore_chars = "[]`_-^" +partial_completion_alert = on +partial_completion_command = off +partial_completion_command_arg = off +partial_completion_count = on +partial_completion_other = off +partial_completion_templates = "config_options" + +[history] +display_default = 5 +max_buffer_lines_minutes = 0 +max_buffer_lines_number = 4096 +max_commands = 100 +max_visited_buffers = 50 + +[proxy] + +[network] +connection_timeout = 60 +gnutls_ca_file = "/etc/ssl/certs/ca-certificates.crt" +gnutls_handshake_timeout = 30 +proxy_curl = "" + +[plugin] +autoload = "*" +debug = off +extension = ".so,.dll" +path = "%h/plugins" +save_config_on_unload = on + +[bar] +buflist.color_bg = default +buflist.color_bg_inactive = default +buflist.color_delim = default +buflist.color_fg = default +buflist.conditions = "" +buflist.filling_left_right = vertical +buflist.filling_top_bottom = columns_vertical +buflist.hidden = off +buflist.items = "buflist" +buflist.position = left +buflist.priority = 0 +buflist.separator = on +buflist.size = 0 +buflist.size_max = 0 +buflist.type = root +fset.color_bg = default +fset.color_bg_inactive = default +fset.color_delim = cyan +fset.color_fg = default +fset.conditions = "${buffer.full_name} == fset.fset" +fset.filling_left_right = vertical +fset.filling_top_bottom = horizontal +fset.hidden = off +fset.items = "fset" +fset.position = top +fset.priority = 0 +fset.separator = on +fset.size = 3 +fset.size_max = 3 +fset.type = window +input.color_bg = default +input.color_bg_inactive = default +input.color_delim = cyan +input.color_fg = default +input.conditions = "" +input.filling_left_right = vertical +input.filling_top_bottom = horizontal +input.hidden = off +input.items = "[input_prompt]+(away),[input_search],[input_paste],input_text" +input.position = bottom +input.priority = 1000 +input.separator = off +input.size = 0 +input.size_max = 0 +input.type = window +nicklist.color_bg = default +nicklist.color_bg_inactive = default +nicklist.color_delim = cyan +nicklist.color_fg = default +nicklist.conditions = "${nicklist}" +nicklist.filling_left_right = vertical +nicklist.filling_top_bottom = columns_vertical +nicklist.hidden = off +nicklist.items = "buffer_nicklist" +nicklist.position = right +nicklist.priority = 200 +nicklist.separator = on +nicklist.size = 0 +nicklist.size_max = 0 +nicklist.type = window +status.color_bg = blue +status.color_bg_inactive = darkgray +status.color_delim = cyan +status.color_fg = default +status.conditions = "" +status.filling_left_right = vertical +status.filling_top_bottom = horizontal +status.hidden = off +status.items = "[time],[buffer_last_number],[buffer_plugin],buffer_number+:+buffer_name+(buffer_modes)+{buffer_nicklist_count}+buffer_zoom+buffer_filter,scroll,[lag],[hotlist],completion" +status.position = bottom +status.priority = 500 +status.separator = off +status.size = 1 +status.size_max = 0 +status.type = window +title.color_bg = blue +title.color_bg_inactive = darkgray +title.color_delim = cyan +title.color_fg = default +title.conditions = "" +title.filling_left_right = vertical +title.filling_top_bottom = horizontal +title.hidden = off +title.items = "buffer_title" +title.position = top +title.priority = 500 +title.separator = off +title.size = 1 +title.size_max = 0 +title.type = window + +[layout] + +[notify] +perl.highmon = none + +[filter] + +[key] +ctrl-? = "/input delete_previous_char" +ctrl-A = "/input move_beginning_of_line" +ctrl-B = "/input move_previous_char" +ctrl-C_ = "/input insert \x1F" +ctrl-Cb = "/input insert \x02" +ctrl-Cc = "/input insert \x03" +ctrl-Ci = "/input insert \x1D" +ctrl-Co = "/input insert \x0F" +ctrl-Cv = "/input insert \x16" +ctrl-D = "/input delete_next_char" +ctrl-E = "/input move_end_of_line" +ctrl-F = "/input move_next_char" +ctrl-H = "/input delete_previous_char" +ctrl-I = "/input complete_next" +ctrl-J = "/input return" +ctrl-K = "/input delete_end_of_line" +ctrl-L = "/window refresh" +ctrl-M = "/input return" +ctrl-N = "/buffer +1" +ctrl-P = "/buffer -1" +ctrl-R = "/input search_text_here" +ctrl-Sctrl-U = "/input set_unread" +ctrl-T = "/input transpose_chars" +ctrl-U = "/input delete_beginning_of_line" +ctrl-W = "/input delete_previous_word" +ctrl-X = "/input switch_active_buffer" +ctrl-Y = "/input clipboard_paste" +meta-ctrl-M = "/input insert \n" +meta-meta-OP = "/bar scroll buflist * b" +meta-meta-OQ = "/bar scroll buflist * e" +meta-meta2-11~ = "/bar scroll buflist * b" +meta-meta2-12~ = "/bar scroll buflist * e" +meta-meta2-1~ = "/window scroll_top" +meta-meta2-23~ = "/bar scroll nicklist * b" +meta-meta2-24~ = "/bar scroll nicklist * e" +meta-meta2-4~ = "/window scroll_bottom" +meta-meta2-5~ = "/window scroll_up" +meta-meta2-6~ = "/window scroll_down" +meta-meta2-7~ = "/window scroll_top" +meta-meta2-8~ = "/window scroll_bottom" +meta-meta2-A = "/buffer -1" +meta-meta2-B = "/buffer +1" +meta-meta2-C = "/buffer +1" +meta-meta2-D = "/buffer -1" +meta-- = "/filter toggle @" +meta-/ = "/input jump_last_buffer_displayed" +meta-0 = "/buffer *10" +meta-1 = "/buffer *1" +meta-2 = "/buffer *2" +meta-3 = "/buffer *3" +meta-4 = "/buffer *4" +meta-5 = "/buffer *5" +meta-6 = "/buffer *6" +meta-7 = "/buffer *7" +meta-8 = "/buffer *8" +meta-9 = "/buffer *9" +meta-< = "/input jump_previously_visited_buffer" +meta-= = "/filter toggle" +meta-> = "/input jump_next_visited_buffer" +meta-B = "/buflist toggle" +meta-N = "/bar toggle nicklist" +meta-OA = "/input history_global_previous" +meta-OB = "/input history_global_next" +meta-OC = "/input move_next_word" +meta-OD = "/input move_previous_word" +meta-OF = "/input move_end_of_line" +meta-OH = "/input move_beginning_of_line" +meta-OP = "/bar scroll buflist * -100%" +meta-OQ = "/bar scroll buflist * +100%" +meta-Oa = "/input history_global_previous" +meta-Ob = "/input history_global_next" +meta-Oc = "/input move_next_word" +meta-Od = "/input move_previous_word" +meta2-11^ = "/bar scroll buflist * -100%" +meta2-11~ = "/bar scroll buflist * -100%" +meta2-12^ = "/bar scroll buflist * +100%" +meta2-12~ = "/bar scroll buflist * +100%" +meta2-15~ = "/buffer -1" +meta2-17~ = "/buffer +1" +meta2-18~ = "/window -1" +meta2-19~ = "/window +1" +meta2-1;3A = "/buffer -1" +meta2-1;3B = "/buffer +1" +meta2-1;3C = "/buffer +1" +meta2-1;3D = "/buffer -1" +meta2-1;3F = "/window scroll_bottom" +meta2-1;3H = "/window scroll_top" +meta2-1;3P = "/bar scroll buflist * b" +meta2-1;3Q = "/bar scroll buflist * e" +meta2-1;5A = "/input history_global_previous" +meta2-1;5B = "/input history_global_next" +meta2-1;5C = "/input move_next_word" +meta2-1;5D = "/input move_previous_word" +meta2-1;5P = "/bar scroll buflist * -100%" +meta2-1;5Q = "/bar scroll buflist * +100%" +meta2-1~ = "/input move_beginning_of_line" +meta2-200~ = "/input paste_start" +meta2-201~ = "/input paste_stop" +meta2-20~ = "/bar scroll title * -30%" +meta2-21~ = "/bar scroll title * +30%" +meta2-23;3~ = "/bar scroll nicklist * b" +meta2-23;5~ = "/bar scroll nicklist * -100%" +meta2-23^ = "/bar scroll nicklist * -100%" +meta2-23~ = "/bar scroll nicklist * -100%" +meta2-24;3~ = "/bar scroll nicklist * e" +meta2-24;5~ = "/bar scroll nicklist * +100%" +meta2-24^ = "/bar scroll nicklist * +100%" +meta2-24~ = "/bar scroll nicklist * +100%" +meta2-3~ = "/input delete_next_char" +meta2-4~ = "/input move_end_of_line" +meta2-5;3~ = "/window scroll_up" +meta2-5~ = "/window page_up" +meta2-6;3~ = "/window scroll_down" +meta2-6~ = "/window page_down" +meta2-7~ = "/input move_beginning_of_line" +meta2-8~ = "/input move_end_of_line" +meta2-A = "/input history_previous" +meta2-B = "/input history_next" +meta2-C = "/input move_next_char" +meta2-D = "/input move_previous_char" +meta2-F = "/input move_end_of_line" +meta2-G = "/window page_down" +meta2-H = "/input move_beginning_of_line" +meta2-I = "/window page_up" +meta2-Z = "/input complete_previous" +meta2-[E = "/buffer -1" +meta-_ = "/input redo" +meta-a = "/input jump_smart" +meta-b = "/input move_previous_word" +meta-d = "/input delete_next_word" +meta-f = "/input move_next_word" +meta-h = "/input hotlist_clear" +meta-jmeta-f = "/buffer -" +meta-jmeta-l = "/buffer +" +meta-jmeta-r = "/server raw" +meta-jmeta-s = "/server jump" +meta-j01 = "/buffer *1" +meta-j02 = "/buffer *2" +meta-j03 = "/buffer *3" +meta-j04 = "/buffer *4" +meta-j05 = "/buffer *5" +meta-j06 = "/buffer *6" +meta-j07 = "/buffer *7" +meta-j08 = "/buffer *8" +meta-j09 = "/buffer *9" +meta-j10 = "/buffer *10" +meta-j11 = "/buffer *11" +meta-j12 = "/buffer *12" +meta-j13 = "/buffer *13" +meta-j14 = "/buffer *14" +meta-j15 = "/buffer *15" +meta-j16 = "/buffer *16" +meta-j17 = "/buffer *17" +meta-j18 = "/buffer *18" +meta-j19 = "/buffer *19" +meta-j20 = "/buffer *20" +meta-j21 = "/buffer *21" +meta-j22 = "/buffer *22" +meta-j23 = "/buffer *23" +meta-j24 = "/buffer *24" +meta-j25 = "/buffer *25" +meta-j26 = "/buffer *26" +meta-j27 = "/buffer *27" +meta-j28 = "/buffer *28" +meta-j29 = "/buffer *29" +meta-j30 = "/buffer *30" +meta-j31 = "/buffer *31" +meta-j32 = "/buffer *32" +meta-j33 = "/buffer *33" +meta-j34 = "/buffer *34" +meta-j35 = "/buffer *35" +meta-j36 = "/buffer *36" +meta-j37 = "/buffer *37" +meta-j38 = "/buffer *38" +meta-j39 = "/buffer *39" +meta-j40 = "/buffer *40" +meta-j41 = "/buffer *41" +meta-j42 = "/buffer *42" +meta-j43 = "/buffer *43" +meta-j44 = "/buffer *44" +meta-j45 = "/buffer *45" +meta-j46 = "/buffer *46" +meta-j47 = "/buffer *47" +meta-j48 = "/buffer *48" +meta-j49 = "/buffer *49" +meta-j50 = "/buffer *50" +meta-j51 = "/buffer *51" +meta-j52 = "/buffer *52" +meta-j53 = "/buffer *53" +meta-j54 = "/buffer *54" +meta-j55 = "/buffer *55" +meta-j56 = "/buffer *56" +meta-j57 = "/buffer *57" +meta-j58 = "/buffer *58" +meta-j59 = "/buffer *59" +meta-j60 = "/buffer *60" +meta-j61 = "/buffer *61" +meta-j62 = "/buffer *62" +meta-j63 = "/buffer *63" +meta-j64 = "/buffer *64" +meta-j65 = "/buffer *65" +meta-j66 = "/buffer *66" +meta-j67 = "/buffer *67" +meta-j68 = "/buffer *68" +meta-j69 = "/buffer *69" +meta-j70 = "/buffer *70" +meta-j71 = "/buffer *71" +meta-j72 = "/buffer *72" +meta-j73 = "/buffer *73" +meta-j74 = "/buffer *74" +meta-j75 = "/buffer *75" +meta-j76 = "/buffer *76" +meta-j77 = "/buffer *77" +meta-j78 = "/buffer *78" +meta-j79 = "/buffer *79" +meta-j80 = "/buffer *80" +meta-j81 = "/buffer *81" +meta-j82 = "/buffer *82" +meta-j83 = "/buffer *83" +meta-j84 = "/buffer *84" +meta-j85 = "/buffer *85" +meta-j86 = "/buffer *86" +meta-j87 = "/buffer *87" +meta-j88 = "/buffer *88" +meta-j89 = "/buffer *89" +meta-j90 = "/buffer *90" +meta-j91 = "/buffer *91" +meta-j92 = "/buffer *92" +meta-j93 = "/buffer *93" +meta-j94 = "/buffer *94" +meta-j95 = "/buffer *95" +meta-j96 = "/buffer *96" +meta-j97 = "/buffer *97" +meta-j98 = "/buffer *98" +meta-j99 = "/buffer *99" +meta-k = "/input grab_key_command" +meta-l = "/window bare" +meta-m = "/mute mouse toggle" +meta-n = "/window scroll_next_highlight" +meta-p = "/window scroll_previous_highlight" +meta-r = "/input delete_line" +meta-s = "/mute spell toggle" +meta-u = "/window scroll_unread" +meta-wmeta-meta2-A = "/window up" +meta-wmeta-meta2-B = "/window down" +meta-wmeta-meta2-C = "/window right" +meta-wmeta-meta2-D = "/window left" +meta-wmeta2-1;3A = "/window up" +meta-wmeta2-1;3B = "/window down" +meta-wmeta2-1;3C = "/window right" +meta-wmeta2-1;3D = "/window left" +meta-wmeta-b = "/window balance" +meta-wmeta-s = "/window swap" +meta-x = "/input zoom_merged_buffer" +meta-z = "/window zoom" +ctrl-_ = "/input undo" + +[key_search] +ctrl-I = "/input search_switch_where" +ctrl-J = "/input search_stop_here" +ctrl-M = "/input search_stop_here" +ctrl-Q = "/input search_stop" +ctrl-R = "/input search_switch_regex" +meta2-A = "/input search_previous" +meta2-B = "/input search_next" +meta-c = "/input search_switch_case" + +[key_cursor] +ctrl-J = "/cursor stop" +ctrl-M = "/cursor stop" +meta-meta2-A = "/cursor move area_up" +meta-meta2-B = "/cursor move area_down" +meta-meta2-C = "/cursor move area_right" +meta-meta2-D = "/cursor move area_left" +meta2-1;3A = "/cursor move area_up" +meta2-1;3B = "/cursor move area_down" +meta2-1;3C = "/cursor move area_right" +meta2-1;3D = "/cursor move area_left" +meta2-A = "/cursor move up" +meta2-B = "/cursor move down" +meta2-C = "/cursor move right" +meta2-D = "/cursor move left" +@item(buffer_nicklist):K = "/window ${_window_number};/kickban ${nick}" +@item(buffer_nicklist):b = "/window ${_window_number};/ban ${nick}" +@item(buffer_nicklist):k = "/window ${_window_number};/kick ${nick}" +@item(buffer_nicklist):q = "/window ${_window_number};/query ${nick};/cursor stop" +@item(buffer_nicklist):w = "/window ${_window_number};/whois ${nick}" +@chat:Q = "hsignal:chat_quote_time_prefix_message;/cursor stop" +@chat:m = "hsignal:chat_quote_message;/cursor stop" +@chat:q = "hsignal:chat_quote_prefix_message;/cursor stop" + +[key_mouse] +@bar(buflist):ctrl-wheeldown = "hsignal:buflist_mouse" +@bar(buflist):ctrl-wheelup = "hsignal:buflist_mouse" +@bar(input):button2 = "/input grab_mouse_area" +@bar(nicklist):button1-gesture-down = "/bar scroll nicklist ${_window_number} +100%" +@bar(nicklist):button1-gesture-down-long = "/bar scroll nicklist ${_window_number} e" +@bar(nicklist):button1-gesture-up = "/bar scroll nicklist ${_window_number} -100%" +@bar(nicklist):button1-gesture-up-long = "/bar scroll nicklist ${_window_number} b" +@chat(fset.fset):button1 = "/window ${_window_number};/fset -go ${_chat_line_y}" +@chat(fset.fset):button2* = "hsignal:fset_mouse" +@chat(fset.fset):wheeldown = "/fset -down 5" +@chat(fset.fset):wheelup = "/fset -up 5" +@chat(script.scripts):button1 = "/window ${_window_number};/script go ${_chat_line_y}" +@chat(script.scripts):button2 = "/window ${_window_number};/script go ${_chat_line_y};/script installremove -q ${script_name_with_extension}" +@chat(script.scripts):wheeldown = "/script down 5" +@chat(script.scripts):wheelup = "/script up 5" +@item(buffer_nicklist):button1 = "/window ${_window_number};/query ${nick}" +@item(buffer_nicklist):button1-gesture-left = "/window ${_window_number};/kick ${nick}" +@item(buffer_nicklist):button1-gesture-left-long = "/window ${_window_number};/kickban ${nick}" +@item(buffer_nicklist):button2 = "/window ${_window_number};/whois ${nick}" +@item(buffer_nicklist):button2-gesture-left = "/window ${_window_number};/ban ${nick}" +@item(buflist):button1* = "hsignal:buflist_mouse" +@item(buflist):button2* = "hsignal:buflist_mouse" +@item(buflist2):button1* = "hsignal:buflist_mouse" +@item(buflist2):button2* = "hsignal:buflist_mouse" +@item(buflist3):button1* = "hsignal:buflist_mouse" +@item(buflist3):button2* = "hsignal:buflist_mouse" +@bar:wheeldown = "/bar scroll ${_bar_name} ${_window_number} +20%" +@bar:wheelup = "/bar scroll ${_bar_name} ${_window_number} -20%" +@chat:button1 = "/window ${_window_number}" +@chat:button1-gesture-left = "/window ${_window_number};/buffer -1" +@chat:button1-gesture-left-long = "/window ${_window_number};/buffer 1" +@chat:button1-gesture-right = "/window ${_window_number};/buffer +1" +@chat:button1-gesture-right-long = "/window ${_window_number};/input jump_last_buffer" +@chat:ctrl-wheeldown = "/window scroll_horiz -window ${_window_number} +10%" +@chat:ctrl-wheelup = "/window scroll_horiz -window ${_window_number} -10%" +@chat:wheeldown = "/window scroll_down -window ${_window_number}" +@chat:wheelup = "/window scroll_up -window ${_window_number}" +@*:button3 = "/cursor go ${_x},${_y}" diff --git a/config/weechat/weechat.log b/config/weechat/weechat.log new file mode 100644 index 00000000..2eb7d2be --- /dev/null +++ b/config/weechat/weechat.log @@ -0,0 +1,51 @@ +[2021-04-04 01:40:21] WeeChat 3.1 (compiled on Mar 11 2021 21:41:58) +[2021-04-04 01:40:21] Reading configuration file sec.conf +[2021-04-04 01:40:27] Reading configuration file weechat.conf +[2021-04-04 01:40:27] Reading configuration file plugins.conf +[2021-04-04 01:40:27] Reading configuration file charset.conf +[2021-04-04 01:40:27] Reading configuration file logger.conf +[2021-04-04 01:40:27] Reading configuration file exec.conf +[2021-04-04 01:40:27] Reading configuration file trigger.conf +[2021-04-04 01:40:27] Reading configuration file spell.conf +[2021-04-04 01:40:27] Reading configuration file alias.conf +[2021-04-04 01:40:27] Reading configuration file buflist.conf +[2021-04-04 01:40:27] Reading configuration file fifo.conf +[2021-04-04 01:40:27] Reading configuration file xfer.conf +[2021-04-04 01:40:27] Reading configuration file irc.conf +[2021-04-04 01:40:27] Reading configuration file relay.conf +[2021-04-04 01:40:27] Reading configuration file tcl.conf +[2021-04-04 01:40:27] Reading configuration file python.conf +[2021-04-04 01:40:27] Reading configuration file colorize_nicks.conf +[2021-04-04 01:40:27] Reading configuration file autosort.conf +[2021-04-04 01:40:27] Writing configuration file autosort.conf +[2021-04-04 01:40:27] Reading configuration file guile.conf +[2021-04-04 01:40:27] Reading configuration file perl.conf +[2021-04-04 01:40:27] Reading configuration file ruby.conf +[2021-04-04 01:40:27] Reading configuration file lua.conf +[2021-04-04 01:40:27] Reading configuration file script.conf +[2021-04-04 01:40:27] Reading configuration file fset.conf +[2021-04-04 01:40:27] irc: connecting to server chat.freenode.net/6697 (SSL)... +[2021-04-04 01:41:47] Writing configuration file plugins.conf +[2021-04-04 01:41:47] Writing configuration file trigger.conf +[2021-04-04 01:41:47] Writing configuration file spell.conf +[2021-04-04 01:41:47] Writing configuration file relay.conf +[2021-04-04 01:41:47] Writing configuration file lua.conf +[2021-04-04 01:41:47] Writing configuration file ruby.conf +[2021-04-04 01:41:47] Writing configuration file xfer.conf +[2021-04-04 01:41:47] Writing configuration file fifo.conf +[2021-04-04 01:41:47] Writing configuration file exec.conf +[2021-04-04 01:41:47] Writing configuration file buflist.conf +[2021-04-04 01:41:47] Writing configuration file logger.conf +[2021-04-04 01:41:47] Writing configuration file perl.conf +[2021-04-04 01:41:47] Writing configuration file charset.conf +[2021-04-04 01:41:47] Writing configuration file alias.conf +[2021-04-04 01:41:47] Writing configuration file fset.conf +[2021-04-04 01:41:47] Writing configuration file guile.conf +[2021-04-04 01:41:47] Writing configuration file autosort.conf +[2021-04-04 01:41:47] Writing configuration file colorize_nicks.conf +[2021-04-04 01:41:47] Writing configuration file python.conf +[2021-04-04 01:41:47] Writing configuration file irc.conf +[2021-04-04 01:41:47] Writing configuration file tcl.conf +[2021-04-04 01:41:47] Writing configuration file script.conf +[2021-04-04 01:41:47] Writing configuration file weechat.conf +[2021-04-04 01:41:47] Writing configuration file sec.conf diff --git a/config/weechat/xfer.conf b/config/weechat/xfer.conf new file mode 100644 index 00000000..551316ff --- /dev/null +++ b/config/weechat/xfer.conf @@ -0,0 +1,49 @@ +# +# weechat -- xfer.conf +# +# WARNING: It is NOT recommended to edit this file by hand, +# especially if WeeChat is running. +# +# Use /set or similar command to change settings in WeeChat. +# +# For more info, see: https://weechat.org/doc/quickstart +# + +[look] +auto_open_buffer = on +progress_bar_size = 20 +pv_tags = "notify_private" + +[color] +status_aborted = lightred +status_active = lightblue +status_connecting = yellow +status_done = lightgreen +status_failed = lightred +status_waiting = lightcyan +text = default +text_bg = default +text_selected = white + +[network] +blocksize = 65536 +fast_send = on +own_ip = "" +port_range = "" +send_ack = on +speed_limit_recv = 0 +speed_limit_send = 0 +timeout = 300 + +[file] +auto_accept_chats = off +auto_accept_files = off +auto_accept_nicks = "" +auto_check_crc32 = off +auto_rename = on +auto_resume = on +convert_spaces = on +download_path = "%h/xfer" +download_temporary_suffix = ".part" +upload_path = "~" +use_nick_in_filename = on