@ -1 +0,0 @@ | |||||
../atcomplete.pl |
@ -0,0 +1,89 @@ | |||||
# Copyright 2015 by David A. Golden. All rights reserved. | |||||
# | |||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may | |||||
# not use this file except in compliance with the License. You may obtain | |||||
# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 | |||||
# | |||||
# ABOUT | |||||
# | |||||
# atcomplete.pl | |||||
# | |||||
# Adds nick completion when prefixed with '@' for use with IRC gateways | |||||
# for Slack, Flowdock, etc. as these require the '@' to highlight users | |||||
# | |||||
# CONFIG | |||||
# | |||||
# /set plugins.var.perl.atcomplete.enabled | |||||
# | |||||
# HISTORY | |||||
# | |||||
# 0.001 -- xdg, 2016-04-06 | |||||
# | |||||
# - initial release | |||||
# | |||||
# REPOSITORY | |||||
# | |||||
# https://github.com/xdg/weechat-atcomplete | |||||
use strict; | |||||
use warnings; | |||||
my $SCRIPT_NAME = "atcomplete"; | |||||
my $VERSION = "0.001"; | |||||
my %options_default = ( | |||||
'enabled' => ['on', 'enable completion of nicks starting with @'], | |||||
); | |||||
my %options = (); | |||||
weechat::register($SCRIPT_NAME, "David A. Golden", $VERSION, | |||||
"Apache2", "atcomplete - do nick completion following @", "", ""); | |||||
init_config(); | |||||
weechat::hook_config("plugins.var.perl.$SCRIPT_NAME.*", "toggle_config_by_set", ""); | |||||
weechat::hook_completion("nicks", "Add @ prefix to nick completion", "complete_at_nicks", ""); | |||||
sub complete_at_nicks { | |||||
my ($data, $completion_item, $buffer, $completion ) = @_; | |||||
return weechat::WEECHAT_RC_OK() unless $options{enabled} eq 'on'; | |||||
my $nicklist = weechat::infolist_get("nicklist", weechat::current_buffer(), ""); | |||||
if ($nicklist ne "") { | |||||
while (weechat::infolist_next($nicklist)) { | |||||
next unless weechat::infolist_string($nicklist, "type") eq "nick"; | |||||
my $nick = weechat::infolist_string($nicklist, "name"); | |||||
weechat::hook_completion_list_add($completion, "\@$nick", 1, weechat::WEECHAT_LIST_POS_SORT()); | |||||
} | |||||
} | |||||
weechat::infolist_free($nicklist); | |||||
return weechat::WEECHAT_RC_OK(); | |||||
} | |||||
sub toggle_config_by_set { | |||||
my ($pointer, $name, $value) = @_; | |||||
$name = substr($name, length("plugins.var.perl.".$SCRIPT_NAME."."), length($name)); | |||||
$options{$name} = $value; | |||||
return weechat::WEECHAT_RC_OK(); | |||||
} | |||||
sub init_config { | |||||
my $version = weechat::info_get("version_number", "") || 0; | |||||
foreach my $option (keys %options_default) | |||||
{ | |||||
if (!weechat::config_is_set_plugin($option)) | |||||
{ | |||||
weechat::config_set_plugin($option, $options_default{$option}[0]); | |||||
$options{$option} = $options_default{$option}[0]; | |||||
} | |||||
else | |||||
{ | |||||
$options{$option} = weechat::config_get_plugin($option); | |||||
} | |||||
if ($version >= 0x00030500) | |||||
{ | |||||
weechat::config_set_desc_plugin($option, $options_default{$option}[1]." (default: \"".$options_default{$option}[0]."\")"); | |||||
} | |||||
} | |||||
} |
@ -1 +0,0 @@ | |||||
../awaylog.pl |
@ -0,0 +1,100 @@ | |||||
############################################################################### | |||||
# | |||||
# 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 +0,0 @@ | |||||
../highmon.pl |
@ -1 +0,0 @@ | |||||
../iset.pl |
@ -1 +0,0 @@ | |||||
../aesthetic.py |
@ -0,0 +1,70 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# | |||||
# Script Name: aesthetic.py | |||||
# Script Author: Wojciech Siewierski | |||||
# Script License: GPL3 | |||||
# Contact: vifon @ irc.freenode.net | |||||
SCRIPT_NAME = 'aesthetic' | |||||
SCRIPT_AUTHOR = 'Wojciech Siewierski' | |||||
SCRIPT_VERSION = '1.0.6' | |||||
SCRIPT_LICENSE = 'GPL3' | |||||
SCRIPT_DESC = 'Make messages more A E S T H E T I C A L L Y pleasing.' | |||||
import_ok = True | |||||
try: | |||||
import weechat | |||||
except ImportError: | |||||
print('This script must be run under WeeChat') | |||||
print('You can obtain a copy of WeeChat, for free, at https://weechat.org') | |||||
import_ok = False | |||||
weechat_version = 0 | |||||
import shlex | |||||
import sys | |||||
def aesthetic_(args): | |||||
for arg in args: | |||||
try: | |||||
arg = arg.decode('utf8') | |||||
except AttributeError: | |||||
pass | |||||
yield " ".join(arg.upper()) | |||||
for n, char in enumerate(arg[1:]): | |||||
yield " ".join(" "*(n+1)).join(char.upper()*2) | |||||
def aesthetic(args): | |||||
if sys.version_info < (3,): | |||||
return (x.encode('utf8') for x in aesthetic_(args)) | |||||
else: | |||||
return aesthetic_(args) | |||||
def aesthetic_cb(data, buffer, args): | |||||
for x in aesthetic(shlex.split(args)): | |||||
weechat.command(buffer, x) | |||||
return weechat.WEECHAT_RC_OK | |||||
if __name__ == "__main__" and import_ok: | |||||
if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): | |||||
weechat_version = weechat.info_get("version_number", "") or 0 | |||||
weechat.hook_command( | |||||
"aesthetic", | |||||
"""Format a message like this: | |||||
E X A M P L E | |||||
X X | |||||
A A | |||||
M M | |||||
P P | |||||
L L | |||||
E E | |||||
Each argument is formatted separately, use sh-like quotes for grouping. For example '/aesthetic foo bar' will send two such blocks while '/aesthetic "foo bar"' would send one larger one. | |||||
Use with care to not cause undesirable message spam.""", | |||||
"message", "", | |||||
"", | |||||
"aesthetic_cb", "" | |||||
) |
@ -1 +0,0 @@ | |||||
../anotify.py |
@ -0,0 +1,476 @@ | |||||
# -*- 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 +0,0 @@ | |||||
../autosort.py |
@ -1 +0,0 @@ | |||||
../colorize_nicks.py |
@ -0,0 +1,409 @@ | |||||
# -*- 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 +0,0 @@ | |||||
../go.py |
@ -0,0 +1,563 @@ | |||||
# -*- 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 +0,0 @@ | |||||
../styurl.py |
@ -0,0 +1,178 @@ | |||||
# -*- 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 +0,0 @@ | |||||
../vimode.py |
@ -1,6 +1,6 @@ | |||||
#!/bin/sh | #!/bin/sh | ||||
title=$(mpc -f "%title%" 2> /dev/null | head -n 1 | xargs) | |||||
artist=$(mpc -f "%artist%" 2> /dev/null | head -n 1 | xargs) | |||||
title=$(mpc -f "%title%" 2> /dev/null | head -n 1 | xargs -0 ) | |||||
artist=$(mpc -f "%artist%" 2> /dev/null | head -n 1 | xargs -0 ) | |||||
notify-send -a " $artist" -t 1500 "~ $title ~ " | notify-send -a " $artist" -t 1500 "~ $title ~ " |
@ -1,8 +1,14 @@ | |||||
#!/bin/bash | #!/bin/bash | ||||
source ~/.config/config.env | |||||
i=0 | |||||
max=0 | |||||
while [ -f "/sys/class/thermal/thermal_zone$i/temp" ]; do | |||||
if [ "$(sed 's/[0-9][0-9][0-9]$//' "/sys/class/thermal/thermal_zone$i/temp")" -gt "$max" ]; then | |||||
max="$(sed 's/[0-9][0-9][0-9]$//' "/sys/class/thermal/thermal_zone$i/temp")" | |||||
fi | |||||
i=$((i+1)) | |||||
done | |||||
temp=$(sed 's/[0-9][0-9][0-9]$/°C/' $TEMP_ZONE) | |||||
icon= | icon= | ||||
echo "^c#bf616a^$icon^d^ $temp" | |||||
echo "^c#bf616a^$icon^d^ $max°C" |
@ -0,0 +1,54 @@ | |||||
#!/bin/sh | |||||
~/.local/bin/daily-update | |||||
dwmblocks > $XDG_RUNTIME_DIR/dwmblocks.out 2> $XDG_RUNTIME_DIR/dwmblocks.err & | |||||
~/.local/bin/keyboard > $XDG_RUNTIME_DIR/keyboard.out 2> $XDG_RUNTIME_DIR/keyboard.err | |||||
~/.local/bin/mailsync & | |||||
if [ "$NEXTCLOUD" = true ] ; then | |||||
nextcloud --background & | |||||
fi | |||||
mkdir -p ~/Downloads/neomutt | |||||
if [ "$MCONNECT" = true ] ; then | |||||
mkdir -p ~/Downloads/mconnect | |||||
(cd ~/Downloads/mconnect; mconnect -d > $XDG_RUNTIME_DIR/mconnect 2> $XDG_RUNTIME_DIR/mconnect.err &) | |||||
fi | |||||
if [ "$ACTIVITYWATCHER" = true ] ; then | |||||
pkill -f aw-watcher-window | |||||
pkill -f aw-watcher-afk | |||||
pkill -f aw-server | |||||
aw-server & | |||||
aw-watcher-window & | |||||
aw-watcher-afk & | |||||
fi | |||||
mpd | |||||
mpd-mpris & | |||||
touch ~/.cache/nextcloud-track | |||||
xss-lock -- slock & | |||||
picom --no-fading-openclose & | |||||
~/.local/bin/firefox-sync | |||||
curl 'http://yeetclock/setcolor?R=136&G=192&B=208' & | |||||
dunst & | |||||
xbanish -s & | |||||
redshift -x 2> /dev/null > /dev/null | |||||
redshift -r -l "$LATLONG" > /dev/null 2> /dev/null & | |||||
tmux new-session -s weechat -d weechat > /dev/null 2> /dev/null | |||||
~/.local/bin/devmon --exec-on-drive "/sbin/notify-send '禍 drive mounted' '%l (%f) at %d '" \ | |||||
--exec-on-remove "/sbin/notify-send '禍 drive removed' '%l (%f) from %d '" \ | |||||
--exec-on-unmount "/sbin/notify-send '禍 drive unmounted' '%l (%f) from %d '" \ | |||||
--no-unmount --no-gui & | |||||
clipmenud > $XDG_RUNTIME_DIR/clipmenud.out 2> $XDG_RUNTIME_DIR/clipmenud.err & | |||||
rm -f ~/.surf/tabbed-surf.xid | |||||
/bin/polkit-dumb-agent & |
@ -1,3 +1,5 @@ | |||||
pinentry-program /usr/bin/pinentry | |||||
no-grab | |||||
allow-preset-passphrase | allow-preset-passphrase | ||||
max-cache-ttl 172800 | max-cache-ttl 172800 | ||||
enable-ssh-support | enable-ssh-support | ||||
@ -0,0 +1,179 @@ | |||||
From 37e970479dc5d40e57fc0cbfeaa5e39941483237 Mon Sep 17 00:00:00 2001 | |||||
From: Gan Ainm <gan.ainm.riomhphost@gmail.com> | |||||
Date: Wed, 10 Jun 2020 10:59:02 +0000 | |||||
Subject: [PATCH] dwm-xdgautostart-6.2.diff | |||||
=================================================================== | |||||
--- | |||||
dwm.1 | 23 +++++++++++++++++ | |||||
dwm.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |||||
2 files changed, 105 insertions(+) | |||||
diff --git a/dwm.1 b/dwm.1 | |||||
index 13b3729..9533aa6 100644 | |||||
--- a/dwm.1 | |||||
+++ b/dwm.1 | |||||
@@ -30,6 +30,14 @@ top left corner. The tags which are applied to one or more windows are | |||||
indicated with an empty square in the top left corner. | |||||
.P | |||||
dwm draws a small border around windows to indicate the focus state. | |||||
+.P | |||||
+On start, dwm can start additional programs that may be specified in two special | |||||
+shell scripts (see the FILES section below), autostart_blocking.sh and | |||||
+autostart.sh. The former is executed first and dwm will wait for its | |||||
+termination before starting. The latter is executed in the background before | |||||
+dwm enters its handler loop. | |||||
+.P | |||||
+Either of these files may be omitted. | |||||
.SH OPTIONS | |||||
.TP | |||||
.B \-v | |||||
@@ -152,6 +160,21 @@ Toggles focused window between floating and tiled state. | |||||
.TP | |||||
.B Mod1\-Button3 | |||||
Resize focused window while dragging. Tiled windows will be toggled to the floating state. | |||||
+.SH FILES | |||||
+The files containing programs to be started along with dwm are searched for in | |||||
+the following directories: | |||||
+.IP "1. $XDG_DATA_HOME/dwm" | |||||
+.IP "2. $HOME/.local/share/dwm" | |||||
+.IP "3. $HOME/.dwm" | |||||
+.P | |||||
+The first existing directory is scanned for any of the autostart files below. | |||||
+.TP 15 | |||||
+autostart.sh | |||||
+This file is started as a shell background process before dwm enters its handler | |||||
+loop. | |||||
+.TP 15 | |||||
+autostart_blocking.sh | |||||
+This file is started before any autostart.sh; dwm waits for its termination. | |||||
.SH CUSTOMIZATION | |||||
dwm is customized by creating a custom config.h and (re)compiling the source | |||||
code. This keeps it fast, secure and simple. | |||||
diff --git a/dwm.c b/dwm.c | |||||
index 4465af1..2156b49 100644 | |||||
--- a/dwm.c | |||||
+++ b/dwm.c | |||||
@@ -29,6 +29,7 @@ | |||||
#include <string.h> | |||||
#include <unistd.h> | |||||
#include <sys/types.h> | |||||
+#include <sys/stat.h> | |||||
#include <sys/wait.h> | |||||
#include <X11/cursorfont.h> | |||||
#include <X11/keysym.h> | |||||
@@ -193,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h); | |||||
static void resizemouse(const Arg *arg); | |||||
static void restack(Monitor *m); | |||||
static void run(void); | |||||
+static void runautostart(void); | |||||
static void scan(void); | |||||
static int sendevent(Client *c, Atom proto); | |||||
static void sendmon(Client *c, Monitor *m); | |||||
@@ -235,7 +237,11 @@ static int xerrorstart(Display *dpy, XErrorEvent *ee); | |||||
static void zoom(const Arg *arg); | |||||
/* variables */ | |||||
+static const char autostartblocksh[] = "autostart_blocking.sh"; | |||||
+static const char autostartsh[] = "autostart.sh"; | |||||
static const char broken[] = "broken"; | |||||
+static const char dwmdir[] = "dwm"; | |||||
+static const char localshare[] = ".local/share"; | |||||
static char stext[256]; | |||||
static int screen; | |||||
static int sw, sh; /* X display screen geometry width, height */ | |||||
@@ -1380,6 +1386,83 @@ run(void) | |||||
handler[ev.type](&ev); /* call handler */ | |||||
} | |||||
+void | |||||
+runautostart(void) | |||||
+{ | |||||
+ char *pathpfx; | |||||
+ char *path; | |||||
+ char *xdgdatahome; | |||||
+ char *home; | |||||
+ struct stat sb; | |||||
+ | |||||
+ if ((home = getenv("HOME")) == NULL) | |||||
+ /* this is almost impossible */ | |||||
+ return; | |||||
+ | |||||
+ /* if $XDG_DATA_HOME is set and not empty, use $XDG_DATA_HOME/dwm, | |||||
+ * otherwise use ~/.local/share/dwm as autostart script directory | |||||
+ */ | |||||
+ xdgdatahome = getenv("XDG_DATA_HOME"); | |||||
+ if (xdgdatahome != NULL && *xdgdatahome != '\0') { | |||||
+ /* space for path segments, separators and nul */ | |||||
+ pathpfx = ecalloc(1, strlen(xdgdatahome) + strlen(dwmdir) + 2); | |||||
+ | |||||
+ if (sprintf(pathpfx, "%s/%s", xdgdatahome, dwmdir) <= 0) { | |||||
+ free(pathpfx); | |||||
+ return; | |||||
+ } | |||||
+ } else { | |||||
+ /* space for path segments, separators and nul */ | |||||
+ pathpfx = ecalloc(1, strlen(home) + strlen(localshare) | |||||
+ + strlen(dwmdir) + 3); | |||||
+ | |||||
+ if (sprintf(pathpfx, "%s/%s/%s", home, localshare, dwmdir) < 0) { | |||||
+ free(pathpfx); | |||||
+ return; | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ /* check if the autostart script directory exists */ | |||||
+ if (! (stat(pathpfx, &sb) == 0 && S_ISDIR(sb.st_mode))) { | |||||
+ /* the XDG conformant path does not exist or is no directory | |||||
+ * so we try ~/.dwm instead | |||||
+ */ | |||||
+ char *pathpfx_new = realloc(pathpfx, strlen(home) + strlen(dwmdir) + 3); | |||||
+ if(pathpfx_new == NULL) { | |||||
+ free(pathpfx); | |||||
+ return; | |||||
+ } | |||||
+ pathpfx = pathpfx_new; | |||||
+ | |||||
+ if (sprintf(pathpfx, "%s/.%s", home, dwmdir) <= 0) { | |||||
+ free(pathpfx); | |||||
+ return; | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ /* try the blocking script first */ | |||||
+ path = ecalloc(1, strlen(pathpfx) + strlen(autostartblocksh) + 2); | |||||
+ if (sprintf(path, "%s/%s", pathpfx, autostartblocksh) <= 0) { | |||||
+ free(path); | |||||
+ free(pathpfx); | |||||
+ } | |||||
+ | |||||
+ if (access(path, X_OK) == 0) | |||||
+ system(path); | |||||
+ | |||||
+ /* now the non-blocking script */ | |||||
+ if (sprintf(path, "%s/%s", pathpfx, autostartsh) <= 0) { | |||||
+ free(path); | |||||
+ free(pathpfx); | |||||
+ } | |||||
+ | |||||
+ if (access(path, X_OK) == 0) | |||||
+ system(strcat(path, " &")); | |||||
+ | |||||
+ free(pathpfx); | |||||
+ free(path); | |||||
+} | |||||
+ | |||||
void | |||||
scan(void) | |||||
{ | |||||
@@ -2142,6 +2223,7 @@ main(int argc, char *argv[]) | |||||
die("pledge"); | |||||
#endif /* __OpenBSD__ */ | |||||
scan(); | |||||
+ runautostart(); | |||||
run(); | |||||
cleanup(); | |||||
XCloseDisplay(dpy); | |||||
-- | |||||
2.27.0 | |||||
@ -0,0 +1,98 @@ | |||||
From b0f56cb0228afc5bda80ea233d1cffe1a19f292f Mon Sep 17 00:00:00 2001 | |||||
From: bakkeby <bakkeby@gmail.com> | |||||
Date: Tue, 7 Apr 2020 11:40:30 +0200 | |||||
Subject: [PATCH] Lose fullscreen on focus change | |||||
--- | |||||
dwm.c | 20 ++++++++++++++------ | |||||
1 file changed, 14 insertions(+), 6 deletions(-) | |||||
diff --git a/dwm.c b/dwm.c | |||||
index 4465af1..a300ba0 100644 | |||||
--- a/dwm.c | |||||
+++ b/dwm.c | |||||
@@ -418,15 +418,16 @@ buttonpress(XEvent *e) | |||||
{ | |||||
unsigned int i, x, click; | |||||
Arg arg = {0}; | |||||
- Client *c; | |||||
+ Client *c, *sel; | |||||
Monitor *m; | |||||
XButtonPressedEvent *ev = &e->xbutton; | |||||
click = ClkRootWin; | |||||
/* focus monitor if necessary */ | |||||
if ((m = wintomon(ev->window)) && m != selmon) { | |||||
- unfocus(selmon->sel, 1); | |||||
+ sel = selmon->sel; | |||||
selmon = m; | |||||
+ unfocus(sel, 1); | |||||
focus(NULL); | |||||
} | |||||
if (ev->window == selmon->barwin) { | |||||
@@ -754,7 +755,7 @@ drawbars(void) | |||||
void | |||||
enternotify(XEvent *e) | |||||
{ | |||||
- Client *c; | |||||
+ Client *c, *sel; | |||||
Monitor *m; | |||||
XCrossingEvent *ev = &e->xcrossing; | |||||
@@ -763,8 +764,9 @@ enternotify(XEvent *e) | |||||
c = wintoclient(ev->window); | |||||
m = c ? c->mon : wintomon(ev->window); | |||||
if (m != selmon) { | |||||
- unfocus(selmon->sel, 1); | |||||
+ sel = selmon->sel; | |||||
selmon = m; | |||||
+ unfocus(sel, 1); | |||||
} else if (!c || c == selmon->sel) | |||||
return; | |||||
focus(c); | |||||
@@ -819,13 +821,15 @@ void | |||||
focusmon(const Arg *arg) | |||||
{ | |||||
Monitor *m; | |||||
+ Client *sel; | |||||
if (!mons->next) | |||||
return; | |||||
if ((m = dirtomon(arg->i)) == selmon) | |||||
return; | |||||
- unfocus(selmon->sel, 0); | |||||
+ sel = selmon->sel; | |||||
selmon = m; | |||||
+ unfocus(sel, 0); | |||||
focus(NULL); | |||||
} | |||||
@@ -1120,13 +1124,15 @@ motionnotify(XEvent *e) | |||||
{ | |||||
static Monitor *mon = NULL; | |||||
Monitor *m; | |||||
+ Client *sel; | |||||
XMotionEvent *ev = &e->xmotion; | |||||
if (ev->window != root) | |||||
return; | |||||
if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { | |||||
- unfocus(selmon->sel, 1); | |||||
+ sel = selmon->sel; | |||||
selmon = m; | |||||
+ unfocus(sel, 1); | |||||
focus(NULL); | |||||
} | |||||
mon = m; | |||||
@@ -1751,6 +1757,8 @@ unfocus(Client *c, int setfocus) | |||||
{ | |||||
if (!c) | |||||
return; | |||||
+ if (c->isfullscreen && ISVISIBLE(c) && c->mon == selmon) | |||||
+ setfullscreen(c, 0); | |||||
grabbuttons(c, 0); | |||||
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); | |||||
if (setfocus) { | |||||
-- | |||||
2.19.1 | |||||