@ -1,24 +0,0 @@ | |||
# | |||
# 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}"]" |
@ -1,20 +0,0 @@ | |||
# | |||
# 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 |
@ -1,38 +0,0 @@ | |||
# | |||
# weechat -- iset.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] | |||
bg_selected = red | |||
help_default_value = green | |||
help_option_name = white | |||
help_text = default | |||
option = default | |||
option_selected = white | |||
type = brown | |||
type_selected = yellow | |||
value = cyan | |||
value_diff = magenta | |||
value_diff_selected = lightmagenta | |||
value_selected = lightcyan | |||
value_undef = green | |||
value_undef_selected = lightgreen | |||
[help] | |||
show_help_bar = on | |||
show_help_extra_info = on | |||
show_plugin_description = off | |||
[look] | |||
scroll_horiz = 10 | |||
show_current_line = on | |||
use_color = off | |||
use_mute = off | |||
value_search_char = "=" |
@ -1,89 +0,0 @@ | |||
# 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]."\")"); | |||
} | |||
} | |||
} |
@ -1,89 +0,0 @@ | |||
# 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]."\")"); | |||
} | |||
} | |||
} |
@ -1,100 +0,0 @@ | |||
############################################################################### | |||
# | |||
# Copyright (c) 2008 by GolemJ <golemj@gmail.com> | |||
# | |||
# 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 2 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, write to the Free Software | |||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||
# | |||
############################################################################### | |||
# | |||
# Log highlights msg to core buffer | |||
# You need to set "notify" to "yes" and "command" to proper command to run | |||
# external command. You also need "shell" script to run external command. | |||
# | |||
# History: | |||
# 2010-06-20, GolemJ <golemj@gmail.com> | |||
# version 0.8, add posibility to execute command for notification | |||
# 2010-02-14, Emmanuel Bouthenot <kolter@openics.org> | |||
# version 0.7, add colors and notifications support | |||
# 2009-05-02, FlashCode <flashcode@flashtux.org>: | |||
# version 0.6, sync with last API changes | |||
# 2008-11-30, GolemJ <golemj@gmail.com>: | |||
# version 0.5, conversion to WeeChat 0.3.0+ | |||
# | |||
############################################################################### | |||
use strict; | |||
weechat::register( "awaylog", "Jiri Golembiovsky", "0.8", "GPL", "Prints highlights to core buffer", "", "" ); | |||
weechat::hook_print( "", "", "", 1, "highlight_cb", "" ); | |||
if( weechat::config_get_plugin( "on_away_only" ) eq "" ) { | |||
weechat::config_set_plugin( "on_away_only", "off" ); | |||
} | |||
if( weechat::config_get_plugin( "plugin_color" ) eq "" ) { | |||
weechat::config_set_plugin( "plugin_color", "default" ); | |||
} | |||
if( weechat::config_get_plugin( "name_color" ) eq "" ) { | |||
weechat::config_set_plugin( "name_color", "default" ); | |||
} | |||
if( weechat::config_get_plugin( "notify" ) eq "" ) { | |||
weechat::config_set_plugin( "notify", "off" ); | |||
} | |||
if( weechat::config_get_plugin( "command" ) eq "") { | |||
weechat::config_set_plugin( "command", "" ); | |||
} | |||
sub highlight_cb { | |||
if( $_[5] == 1 ) { | |||
my $away = weechat::buffer_get_string($_[1], "localvar_away"); | |||
if (($away ne "") || (weechat::config_get_plugin( "on_away_only" ) ne "on")) | |||
{ | |||
my $buffer_color = weechat::color(weechat::config_get_plugin( "plugin_color")) | |||
. weechat::buffer_get_string($_[1], "plugin") | |||
. "." | |||
. weechat::buffer_get_string($_[1], "name") | |||
. weechat::color("default"); | |||
my $buffer = weechat::buffer_get_string($_[1], "plugin") | |||
. "." | |||
. weechat::buffer_get_string($_[1], "name"); | |||
my $name_color = weechat::color(weechat::config_get_plugin( "name_color")) | |||
. $_[6] | |||
. weechat::color("default"); | |||
my $name = $_[6]; | |||
my $message_color = "${buffer_color} -- ${name_color} :: $_[7]"; | |||
my $message = "${buffer} -- ${name} :: $_[7]"; | |||
if( weechat::config_get_plugin( "notify" ) ne "on" ) { | |||
my $command = weechat::config_get_plugin( "command" ); | |||
if( $command ne "" ) { | |||
if( $command =~ /\$msg/ ) { | |||
$command =~ s/\$msg/\'$message\'/; | |||
} else { | |||
$command = "$command '$message'"; | |||
} | |||
weechat::command( "", "/shell $command" ); | |||
} else { | |||
weechat::print("", $message_color); | |||
} | |||
} else { | |||
weechat::print_date_tags("", 0, "notify_highlight", $message_color); | |||
} | |||
} | |||
} | |||
return weechat::WEECHAT_RC_OK; | |||
} |
@ -1,100 +0,0 @@ | |||
############################################################################### | |||
# | |||
# Copyright (c) 2008 by GolemJ <golemj@gmail.com> | |||
# | |||
# 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 2 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, write to the Free Software | |||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||
# | |||
############################################################################### | |||
# | |||
# Log highlights msg to core buffer | |||
# You need to set "notify" to "yes" and "command" to proper command to run | |||
# external command. You also need "shell" script to run external command. | |||
# | |||
# History: | |||
# 2010-06-20, GolemJ <golemj@gmail.com> | |||
# version 0.8, add posibility to execute command for notification | |||
# 2010-02-14, Emmanuel Bouthenot <kolter@openics.org> | |||
# version 0.7, add colors and notifications support | |||
# 2009-05-02, FlashCode <flashcode@flashtux.org>: | |||
# version 0.6, sync with last API changes | |||
# 2008-11-30, GolemJ <golemj@gmail.com>: | |||
# version 0.5, conversion to WeeChat 0.3.0+ | |||
# | |||
############################################################################### | |||
use strict; | |||
weechat::register( "awaylog", "Jiri Golembiovsky", "0.8", "GPL", "Prints highlights to core buffer", "", "" ); | |||
weechat::hook_print( "", "", "", 1, "highlight_cb", "" ); | |||
if( weechat::config_get_plugin( "on_away_only" ) eq "" ) { | |||
weechat::config_set_plugin( "on_away_only", "off" ); | |||
} | |||
if( weechat::config_get_plugin( "plugin_color" ) eq "" ) { | |||
weechat::config_set_plugin( "plugin_color", "default" ); | |||
} | |||
if( weechat::config_get_plugin( "name_color" ) eq "" ) { | |||
weechat::config_set_plugin( "name_color", "default" ); | |||
} | |||
if( weechat::config_get_plugin( "notify" ) eq "" ) { | |||
weechat::config_set_plugin( "notify", "off" ); | |||
} | |||
if( weechat::config_get_plugin( "command" ) eq "") { | |||
weechat::config_set_plugin( "command", "" ); | |||
} | |||
sub highlight_cb { | |||
if( $_[5] == 1 ) { | |||
my $away = weechat::buffer_get_string($_[1], "localvar_away"); | |||
if (($away ne "") || (weechat::config_get_plugin( "on_away_only" ) ne "on")) | |||
{ | |||
my $buffer_color = weechat::color(weechat::config_get_plugin( "plugin_color")) | |||
. weechat::buffer_get_string($_[1], "plugin") | |||
. "." | |||
. weechat::buffer_get_string($_[1], "name") | |||
. weechat::color("default"); | |||
my $buffer = weechat::buffer_get_string($_[1], "plugin") | |||
. "." | |||
. weechat::buffer_get_string($_[1], "name"); | |||
my $name_color = weechat::color(weechat::config_get_plugin( "name_color")) | |||
. $_[6] | |||
. weechat::color("default"); | |||
my $name = $_[6]; | |||
my $message_color = "${buffer_color} -- ${name_color} :: $_[7]"; | |||
my $message = "${buffer} -- ${name} :: $_[7]"; | |||
if( weechat::config_get_plugin( "notify" ) ne "on" ) { | |||
my $command = weechat::config_get_plugin( "command" ); | |||
if( $command ne "" ) { | |||
if( $command =~ /\$msg/ ) { | |||
$command =~ s/\$msg/\'$message\'/; | |||
} else { | |||
$command = "$command '$message'"; | |||
} | |||
weechat::command( "", "/shell $command" ); | |||
} else { | |||
weechat::print("", $message_color); | |||
} | |||
} else { | |||
weechat::print_date_tags("", 0, "notify_highlight", $message_color); | |||
} | |||
} | |||
} | |||
return weechat::WEECHAT_RC_OK; | |||
} |
@ -1,70 +0,0 @@ | |||
# -*- 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", "" | |||
) |
@ -1,476 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# anotify.py | |||
# Copyright (c) 2012 magnific0 <jacco.geul@gmail.com> | |||
# | |||
# based on: | |||
# growl.py | |||
# Copyright (c) 2011 Sorin Ionescu <sorin.ionescu@gmail.com> | |||
# | |||
# 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 weechat | |||
import gi | |||
import notify2 | |||
import subprocess | |||
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() | |||
subprocess.Popen(["notify-send", "-a", " WeeChat", title, description]) | |||
if title != "Server Connected" and title != "Server Disconnected": | |||
subprocess.Popen(["canberra-gtk-play", "-i", "message-new-instant", "-V", "15"]) | |||
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() |
@ -1,70 +0,0 @@ | |||
# -*- 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", "" | |||
) |
@ -1,476 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# anotify.py | |||
# Copyright (c) 2012 magnific0 <jacco.geul@gmail.com> | |||
# | |||
# based on: | |||
# growl.py | |||
# Copyright (c) 2011 Sorin Ionescu <sorin.ionescu@gmail.com> | |||
# | |||
# 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 weechat | |||
import gi | |||
import notify2 | |||
import subprocess | |||
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() | |||
subprocess.Popen(["notify-send", "-a", " WeeChat", title, description]) | |||
if title != "Server Connected" and title != "Server Disconnected": | |||
subprocess.Popen(["canberra-gtk-play", "-i", "message-new-instant", "-V", "15"]) | |||
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() |
@ -1,409 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (c) 2010 by xt <xt@bash.no> | |||
# | |||
# 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 <http://www.gnu.org/licenses/>. | |||
# | |||
# 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 <flashcode@flashtux.org> | |||
# version 27: add compatibility with new weechat_print modifier data | |||
# (WeeChat >= 2.9) | |||
# 2018-04-06: Joey Pabalinas <joeypabalinas@gmail.com> | |||
# 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 <louis.beziaud@ens-rennes.fr> | |||
# version 24: colorize utf8 nicks | |||
# 2017-03-01, arza <arza@arza.us> | |||
# version 23: don't colorize nicklist group names | |||
# 2016-05-01, Simmo Saan <simmo.saan@gmail.com> | |||
# 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 <xt@bash.no>" | |||
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', '') |
@ -1,563 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (C) 2009-2014 Sébastien Helleu <flashcode@flashtux.org> | |||
# Copyright (C) 2010 m4v <lambdae2@gmail.com> | |||
# Copyright (C) 2011 stfn <stfnmd@googlemail.com> | |||
# | |||
# 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 <http://www.gnu.org/licenses/>. | |||
# | |||
# | |||
# History: | |||
# | |||
# 2019-07-11, Simmo Saan <simmo.saan@gmail.com> | |||
# version 2.6: fix detection of "/input search_text_here" | |||
# 2017-04-01, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 2.5: add option "buffer_number" | |||
# 2017-03-02, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 2.4: fix syntax and indentation error | |||
# 2017-02-25, Simmo Saan <simmo.saan@gmail.com> | |||
# version 2.3: fix fuzzy search breaking buffer number search display | |||
# 2016-01-28, ylambda <ylambda@koalabeast.com> | |||
# version 2.2: add option "fuzzy_search" | |||
# 2015-11-12, nils_2 <weechatter@arcor.de> | |||
# 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 <flashcode@flashtux.org>: | |||
# 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 <anti.teamidiot.de> | |||
# version 1.9: add auto_jump option to automatically go to buffer when it | |||
# is uniquely selected | |||
# 2012-09-17, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.8: fix jump to non-active merged buffers (jump with buffer name | |||
# instead of number) | |||
# 2012-01-03 nils_2 <weechatter@arcor.de> | |||
# version 1.7: add option "use_core_instead_weechat" | |||
# 2012-01-03, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.6: make script compatible with Python 3.x | |||
# 2011-08-24, stfn <stfnmd@googlemail.com>: | |||
# version 1.5: /go with name argument jumps directly to buffer | |||
# Remember cursor position in buffer input | |||
# 2011-05-31, Elián Hanisch <lambdae2@gmail.com>: | |||
# version 1.4: Sort list of buffers by activity. | |||
# 2011-04-25, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.3: add info "go_running" (used by script input_lock.rb) | |||
# 2010-11-01, Sébastien Helleu <flashcode@flashtux.org>: | |||
# 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 <lambdae2@gmail.com>: | |||
# version 1.1: use a space to match the end of a string | |||
# 2009-11-16, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.0: add new option to display short names | |||
# 2009-06-15, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.9: fix typo in /help go with command /key | |||
# 2009-05-16, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.8: search buffer by number, fix bug when window is split | |||
# 2009-05-03, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.7: eat tab key (do not complete input, just move buffer | |||
# pointer) | |||
# 2009-05-02, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.6: sync with last API changes | |||
# 2009-03-22, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.5: update modifier signal name for input text display, | |||
# fix arguments for function string_remove_color | |||
# 2009-02-18, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.4: do not hook command and init options if register failed | |||
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.3: case insensitive search for buffers names | |||
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.2: add help about Tab key | |||
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>: | |||
# 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 <flashcode@flashtux.org>' | |||
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() |
@ -1,178 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (C) 2019 Cole Helbling <cole.e.helbling@outlook.com> | |||
# | |||
# 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 <http://www.gnu.org/licenses/>. | |||
# | |||
# Changelog: | |||
# 2019-12-14, Cole Helbling <cole.e.helbling@outlook.com> | |||
# version 1.0: initial release | |||
SCRIPT_NAME = "styurl" | |||
SCRIPT_AUTHOR = "Cole Helbling <cole.e.helbling@outlook.com>" | |||
SCRIPT_VERSION = "1.0" | |||
SCRIPT_LICENSE = "GPL3" | |||
SCRIPT_DESC = "Style URLs with a Python regex" | |||
import_ok = True | |||
try: | |||
import weechat as w | |||
except ImportError: | |||
print("This script must be run under WeeChat.") | |||
print("Get WeeChat now at: https://weechat.org") | |||
import_ok = False | |||
try: | |||
import re | |||
except ImportError as message: | |||
print("Missing package for %s: %s" % (SCRIPT_NAME, message)) | |||
import_ok = False | |||
# https://mathiasbynens.be/demo/url-regex | |||
# If you don't want to create your own regex, see the above link for options or | |||
# ideas on creating your own | |||
styurl_settings = { | |||
"buffer_type": ( | |||
"formatted", | |||
"the type of buffers to run on (options are \"formatted\", \"free\", " | |||
"or \"*\" for both)" | |||
), | |||
"format": ( | |||
"${color:*_32}", | |||
"the style that should be applied to the URL" | |||
"(evaluated, see /help eval)" | |||
), | |||
"ignored_buffers": ( | |||
"core.weechat,python.grep", | |||
"comma-separated list of buffers to ignore URLs in " | |||
"(full name like \"irc.freenode.#alacritty\")" | |||
), | |||
"ignored_tags": ( | |||
"irc_quit,irc_join", | |||
"comma-separated list of tags to ignore URLs from" | |||
), | |||
"regex": ( | |||
r"((?:https?|ftp)://[^\s/$.?#].\S*)", | |||
"the URL-parsing regex using Python syntax " | |||
"(make sure capturing group 1 is the full URL)" | |||
), | |||
} | |||
line_hook = None | |||
def styurl_line_cb(data, line): | |||
""" | |||
Callback called when a line is displayed. | |||
This parses the message for any URLs and styles them according to | |||
styurl_settings["format"]. | |||
""" | |||
global styurl_settings | |||
# Don't style the line if it's not going to be displayed... duh | |||
if line["displayed"] != "1": | |||
return line | |||
tags = line["tags"].split(',') | |||
ignored_tags = styurl_settings["ignored_tags"] | |||
# Ignore specified message tags | |||
if ignored_tags: | |||
if any(tag in tags for tag in ignored_tags.split(',')): | |||
return line | |||
bufname = line["buffer_name"] | |||
ignored_buffers = styurl_settings["ignored_buffers"] | |||
# Ignore specified buffers | |||
if ignored_buffers and bufname in ignored_buffers.split(','): | |||
return line | |||
message = line["message"] | |||
# TODO: enforce presence of a properly-formatted color object at | |||
# styurl_settings["format"] (eval object would also be valid, if it eval'd | |||
# to a color) | |||
regex = re.compile(styurl_settings["regex"]) | |||
url_style = w.string_eval_expression(styurl_settings["format"], {}, {}, {}) | |||
reset = w.color("reset") | |||
# Search for URLs and surround them with the defined URL styling | |||
formatted = regex.sub(r"%s\1%s" % (url_style, reset), message) | |||
line["message"] = line["message"].replace(message, formatted) | |||
return line | |||
def styurl_config_cb(data, option, value): | |||
"""Callback called when a script option is changed.""" | |||
global styurl_settings, line_hook | |||
pos = option.rfind('.') | |||
if pos > 0: | |||
name = option[pos+1:] | |||
if name in styurl_settings: | |||
# Changing the buffer target requires us to re-hook to prevent | |||
# obsolete buffer types from getting styled | |||
if name == "buffer_type": | |||
if value in ("free", "formatted", "*"): | |||
w.unhook(line_hook) | |||
line_hook = w.hook_line(value, "", "", "styurl_line_cb", | |||
"") | |||
else: | |||
# Don't change buffer type if it is invalid | |||
w.prnt("", SCRIPT_NAME + ": Invalid buffer type: '%s', " | |||
"not changing." % value) | |||
w.config_set_plugin(name, styurl_settings[name]) | |||
return w.WEECHAT_RC_ERROR | |||
styurl_settings[name] = value | |||
return w.WEECHAT_RC_OK | |||
def styurl_unload_cb(): | |||
"""Callback called when the script is unloaded.""" | |||
global line_hook | |||
w.unhook(line_hook) | |||
del line_hook | |||
return w.WEECHAT_RC_OK | |||
if __name__ == "__main__" and import_ok: | |||
if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, | |||
SCRIPT_DESC, "styurl_unload_cb", ""): | |||
version = w.info_get("version_number", "") or 0 | |||
for option, value in styurl_settings.items(): | |||
if w.config_is_set_plugin(option): | |||
styurl_settings[option] = w.config_get_plugin(option) | |||
else: | |||
w.config_set_plugin(option, value[0]) | |||
styurl_settings[option] = value[0] | |||
if int(version) >= 0x00030500: | |||
w.config_set_desc_plugin(option, "%s (default: \"%s\")" | |||
% (value[1], value[0])) | |||
w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", | |||
"styurl_config_cb", "") | |||
# Style URLs | |||
line_hook = w.hook_line(styurl_settings["buffer_type"], "", "", | |||
"styurl_line_cb", "") |
@ -1,409 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (c) 2010 by xt <xt@bash.no> | |||
# | |||
# 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 <http://www.gnu.org/licenses/>. | |||
# | |||
# 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 <flashcode@flashtux.org> | |||
# version 27: add compatibility with new weechat_print modifier data | |||
# (WeeChat >= 2.9) | |||
# 2018-04-06: Joey Pabalinas <joeypabalinas@gmail.com> | |||
# 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 <louis.beziaud@ens-rennes.fr> | |||
# version 24: colorize utf8 nicks | |||
# 2017-03-01, arza <arza@arza.us> | |||
# version 23: don't colorize nicklist group names | |||
# 2016-05-01, Simmo Saan <simmo.saan@gmail.com> | |||
# 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 <xt@bash.no>" | |||
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', '') |
@ -1,563 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (C) 2009-2014 Sébastien Helleu <flashcode@flashtux.org> | |||
# Copyright (C) 2010 m4v <lambdae2@gmail.com> | |||
# Copyright (C) 2011 stfn <stfnmd@googlemail.com> | |||
# | |||
# 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 <http://www.gnu.org/licenses/>. | |||
# | |||
# | |||
# History: | |||
# | |||
# 2019-07-11, Simmo Saan <simmo.saan@gmail.com> | |||
# version 2.6: fix detection of "/input search_text_here" | |||
# 2017-04-01, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 2.5: add option "buffer_number" | |||
# 2017-03-02, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 2.4: fix syntax and indentation error | |||
# 2017-02-25, Simmo Saan <simmo.saan@gmail.com> | |||
# version 2.3: fix fuzzy search breaking buffer number search display | |||
# 2016-01-28, ylambda <ylambda@koalabeast.com> | |||
# version 2.2: add option "fuzzy_search" | |||
# 2015-11-12, nils_2 <weechatter@arcor.de> | |||
# 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 <flashcode@flashtux.org>: | |||
# 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 <anti.teamidiot.de> | |||
# version 1.9: add auto_jump option to automatically go to buffer when it | |||
# is uniquely selected | |||
# 2012-09-17, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.8: fix jump to non-active merged buffers (jump with buffer name | |||
# instead of number) | |||
# 2012-01-03 nils_2 <weechatter@arcor.de> | |||
# version 1.7: add option "use_core_instead_weechat" | |||
# 2012-01-03, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.6: make script compatible with Python 3.x | |||
# 2011-08-24, stfn <stfnmd@googlemail.com>: | |||
# version 1.5: /go with name argument jumps directly to buffer | |||
# Remember cursor position in buffer input | |||
# 2011-05-31, Elián Hanisch <lambdae2@gmail.com>: | |||
# version 1.4: Sort list of buffers by activity. | |||
# 2011-04-25, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.3: add info "go_running" (used by script input_lock.rb) | |||
# 2010-11-01, Sébastien Helleu <flashcode@flashtux.org>: | |||
# 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 <lambdae2@gmail.com>: | |||
# version 1.1: use a space to match the end of a string | |||
# 2009-11-16, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 1.0: add new option to display short names | |||
# 2009-06-15, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.9: fix typo in /help go with command /key | |||
# 2009-05-16, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.8: search buffer by number, fix bug when window is split | |||
# 2009-05-03, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.7: eat tab key (do not complete input, just move buffer | |||
# pointer) | |||
# 2009-05-02, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.6: sync with last API changes | |||
# 2009-03-22, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.5: update modifier signal name for input text display, | |||
# fix arguments for function string_remove_color | |||
# 2009-02-18, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.4: do not hook command and init options if register failed | |||
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.3: case insensitive search for buffers names | |||
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>: | |||
# version 0.2: add help about Tab key | |||
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>: | |||
# 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 <flashcode@flashtux.org>' | |||
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() |
@ -1,178 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (C) 2019 Cole Helbling <cole.e.helbling@outlook.com> | |||
# | |||
# 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 <http://www.gnu.org/licenses/>. | |||
# | |||
# Changelog: | |||
# 2019-12-14, Cole Helbling <cole.e.helbling@outlook.com> | |||
# version 1.0: initial release | |||
SCRIPT_NAME = "styurl" | |||
SCRIPT_AUTHOR = "Cole Helbling <cole.e.helbling@outlook.com>" | |||
SCRIPT_VERSION = "1.0" | |||
SCRIPT_LICENSE = "GPL3" | |||
SCRIPT_DESC = "Style URLs with a Python regex" | |||
import_ok = True | |||
try: | |||
import weechat as w | |||
except ImportError: | |||
print("This script must be run under WeeChat.") | |||
print("Get WeeChat now at: https://weechat.org") | |||
import_ok = False | |||
try: | |||
import re | |||
except ImportError as message: | |||
print("Missing package for %s: %s" % (SCRIPT_NAME, message)) | |||
import_ok = False | |||
# https://mathiasbynens.be/demo/url-regex | |||
# If you don't want to create your own regex, see the above link for options or | |||
# ideas on creating your own | |||
styurl_settings = { | |||
"buffer_type": ( | |||
"formatted", | |||
"the type of buffers to run on (options are \"formatted\", \"free\", " | |||
"or \"*\" for both)" | |||
), | |||
"format": ( | |||
"${color:*_32}", | |||
"the style that should be applied to the URL" | |||
"(evaluated, see /help eval)" | |||
), | |||
"ignored_buffers": ( | |||
"core.weechat,python.grep", | |||
"comma-separated list of buffers to ignore URLs in " | |||
"(full name like \"irc.freenode.#alacritty\")" | |||
), | |||
"ignored_tags": ( | |||
"irc_quit,irc_join", | |||
"comma-separated list of tags to ignore URLs from" | |||
), | |||
"regex": ( | |||
r"((?:https?|ftp)://[^\s/$.?#].\S*)", | |||
"the URL-parsing regex using Python syntax " | |||
"(make sure capturing group 1 is the full URL)" | |||
), | |||
} | |||
line_hook = None | |||
def styurl_line_cb(data, line): | |||
""" | |||
Callback called when a line is displayed. | |||
This parses the message for any URLs and styles them according to | |||
styurl_settings["format"]. | |||
""" | |||
global styurl_settings | |||
# Don't style the line if it's not going to be displayed... duh | |||
if line["displayed"] != "1": | |||
return line | |||
tags = line["tags"].split(',') | |||
ignored_tags = styurl_settings["ignored_tags"] | |||
# Ignore specified message tags | |||
if ignored_tags: | |||
if any(tag in tags for tag in ignored_tags.split(',')): | |||
return line | |||
bufname = line["buffer_name"] | |||
ignored_buffers = styurl_settings["ignored_buffers"] | |||
# Ignore specified buffers | |||
if ignored_buffers and bufname in ignored_buffers.split(','): | |||
return line | |||
message = line["message"] | |||
# TODO: enforce presence of a properly-formatted color object at | |||
# styurl_settings["format"] (eval object would also be valid, if it eval'd | |||
# to a color) | |||
regex = re.compile(styurl_settings["regex"]) | |||
url_style = w.string_eval_expression(styurl_settings["format"], {}, {}, {}) | |||
reset = w.color("reset") | |||
# Search for URLs and surround them with the defined URL styling | |||
formatted = regex.sub(r"%s\1%s" % (url_style, reset), message) | |||
line["message"] = line["message"].replace(message, formatted) | |||
return line | |||
def styurl_config_cb(data, option, value): | |||
"""Callback called when a script option is changed.""" | |||
global styurl_settings, line_hook | |||
pos = option.rfind('.') | |||
if pos > 0: | |||
name = option[pos+1:] | |||
if name in styurl_settings: | |||
# Changing the buffer target requires us to re-hook to prevent | |||
# obsolete buffer types from getting styled | |||
if name == "buffer_type": | |||
if value in ("free", "formatted", "*"): | |||
w.unhook(line_hook) | |||
line_hook = w.hook_line(value, "", "", "styurl_line_cb", | |||
"") | |||
else: | |||
# Don't change buffer type if it is invalid | |||
w.prnt("", SCRIPT_NAME + ": Invalid buffer type: '%s', " | |||
"not changing." % value) | |||
w.config_set_plugin(name, styurl_settings[name]) | |||
return w.WEECHAT_RC_ERROR | |||
styurl_settings[name] = value | |||
return w.WEECHAT_RC_OK | |||
def styurl_unload_cb(): | |||
"""Callback called when the script is unloaded.""" | |||
global line_hook | |||
w.unhook(line_hook) | |||
del line_hook | |||
return w.WEECHAT_RC_OK | |||
if __name__ == "__main__" and import_ok: | |||
if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, | |||
SCRIPT_DESC, "styurl_unload_cb", ""): | |||
version = w.info_get("version_number", "") or 0 | |||
for option, value in styurl_settings.items(): | |||
if w.config_is_set_plugin(option): | |||
styurl_settings[option] = w.config_get_plugin(option) | |||
else: | |||
w.config_set_plugin(option, value[0]) | |||
styurl_settings[option] = value[0] | |||
if int(version) >= 0x00030500: | |||
w.config_set_desc_plugin(option, "%s (default: \"%s\")" | |||
% (value[1], value[0])) | |||
w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", | |||
"styurl_config_cb", "") | |||
# Style URLs | |||
line_hook = w.hook_line(styurl_settings["buffer_type"], "", "", | |||
"styurl_line_cb", "") |