From f0a482fc8ffa3999a8291a9263a06d301459c115 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Tue, 11 Nov 2008 11:28:15 +0100
Subject: [PATCH 01/21] New script url_shorten.rb

---
 ruby/url_shorten.rb | 104 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 ruby/url_shorten.rb

diff --git a/ruby/url_shorten.rb b/ruby/url_shorten.rb
new file mode 100644
index 00000000..2aedc844
--- /dev/null
+++ b/ruby/url_shorten.rb
@@ -0,0 +1,104 @@
+# Copyright (c) 2008, Daniel Bretoi <daniel@bretoi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY Daniel Bretoi ''AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# * Simpler, more robust version of tinyurl (doesn't rely on html output)
+# * Echo's urls from all channels that are over the plugin's maxlen value
+#     in a shortened format.
+# * allows for manual shortening of urls
+# * set the shortener function alias to point to favorite shortener (qurl,tinyurl)
+
+require 'net/http'
+require 'uri'
+
+def weechat_init
+  Weechat.register "url_shorten", "1.0", "", "Shorten url"
+  Weechat.add_command_handler "url_shorten", "url_shorten","/url_shorten <url>"
+  Weechat.add_message_handler "privmsg", "msg_shorten"
+  if ( maxlen = Weechat.get_plugin_config("maxlen") ).empty?
+    Weechat.set_plugin_config("maxlen","50")
+  end
+  return Weechat::PLUGIN_RC_OK
+end
+
+def fetch(uri_str, limit = 10)
+  raise ArgumentError, 'HTTP redirect too deep' if limit == 0
+
+  response = Net::HTTP.get_response(URI.parse(uri_str))
+  case response
+  when Net::HTTPSuccess     then response.body
+  when Net::HTTPRedirection then fetch(response['location'], limit - 1)
+  else
+    response.error!
+  end
+end
+
+def qurl_shorten(url)
+  fetch('http://www.qurl.com/automate.php?url='+url).gsub('www.','')
+end
+
+def tinyurl_shorten(url)
+  fetch('http://tinyurl.com/api-create.php?url='+url)
+end
+alias shortener qurl_shorten
+
+def regexp_url
+  @regexp_url ||= Regexp.new('https?://[^\s]*')
+  @regexp_url
+end
+
+def url_shorten(server,msg)
+  if (msg.empty?)
+    usage
+    return Weechat::PLUGIN_RC_OK
+  end
+  url = (msg.scan regexp_url).to_s
+  short = shortener(url)
+  Weechat::print("\x0305#{short}\x0F");
+  return Weechat::PLUGIN_RC_OK
+end
+
+def msg_shorten(server,args)
+  if (args.empty?)
+    usage
+    return Weechat::PLUGIN_RC_OK
+  end
+
+  null,info,msg = args.split(":",3)
+  mask,type,chan = info.split(" ")
+
+  return Weechat::PLUGIN_RC_OK unless msg.match regexp_url
+
+  url = (msg.scan regexp_url).to_s
+
+  maxlen = Weechat.get_plugin_config "maxlen"
+  return Weechat::PLUGIN_RC_OK if url.length < maxlen.to_i
+  short = shortener(url)
+
+  Weechat::print("\x0305#{short}\x0F",chan,server);
+  return Weechat::PLUGIN_RC_OK
+end
+
+def usage
+  Weechat.print %|
+    /url_shorten <url>
+  |
+end

From c17bd4bf75e3b1aefe546011aa9640b791605649 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 1 Dec 2008 18:04:59 +0100
Subject: [PATCH 02/21] New version of crypt.py for v0.2.6

---
 python/crypt.py | 101 +++++++++++++++++++++++++++---------------------
 1 file changed, 58 insertions(+), 43 deletions(-)

diff --git a/python/crypt.py b/python/crypt.py
index 59f75fc5..c21fa9a2 100644
--- a/python/crypt.py
+++ b/python/crypt.py
@@ -1,11 +1,11 @@
 #!/usr/bin/python
 # ==============================================================
-# crypt.py written 08/2008 by blackpenguin <nlissne@linux01.org>
+# crypt.py written 2008 by blackpenguin <nlissne@linux01.org>
 # ==============================================================
-# License     : Public Domain
+# License     : GPL3
 # Description : encrypt/decrypt PRIVMSGs in WeeChat using openssl
 #
-# Version     : 0.02
+version="1.0-0.2.6"
 #
 # This plugin uses openssl to encrypt/decrypt messages you send
 # or receive with weechat. Due to the very simple method 
@@ -14,74 +14,89 @@
 #
 # The default encryption algorithm is blowfish, but you can
 # easily change it to any other cipher your openssl offers.
-# Read output of "openssl -h" to find out, which ciphers are
-# supported (also depends on your kernel)
+# Read output of "openssl -h" to find out which ciphers are
+# supported (also depends on your kernel!)
 CIPHER="blowfish"
 # 
-# To activate encryption for a given server.user just
-# put a file called "cryptkey.server.user" into 
+# To activate encryption for a given user just
+# put a file called "cryptkey.username" into 
 # your weechat-directory, containing the passphrase
 # to use for encryption/decryption 
+# 
+# You can activate encryption on irc-channels, too,
+# just use cryptkey.#channelname as keyfile then.
+#
 # example: if you have exchanged a secret key with me,
 # you would put it in a file called 
-# cryptkey.freenode.blackpenguin in your weechat_dir
+# cryptkey.blackpenguin in your weechat_dir
 #
 # Of course, you need to share this keyfile with the 
 # remote side in another secure way (i.e. sending
 # pgp-encrypted mail)
-#
-# I might implement a /crypt command to activate/deactivate
-# encryption later, but for now the method used works for me.
-#
-# HISTORY:
-# version 0.01 initial version
-#
-# version 0.02 switched from os.environ["HOME"] + "/.weechat" 
-#              to get_info("weechat_dir")
-
-import weechat, string, os
 
+import weechat, string, os, subprocess
 
 def decrypt(server, args):
-  pre, middle, message = string.split(args, ":", 2)
-  midstr=middle.split(" ")
-  username=midstr[-2]
-  if os.path.exists(weechat_dir + "/cryptkey." + server + "." + username):
-    cin, cout = os.popen2("openssl enc -d -a -" + CIPHER + " -pass file:" + weechat_dir + "/cryptkey." + server + "." + username + " 2>/dev/null")
-    cin.write(message.replace("|","\n"))
-    cin.close()
-    decrypted = cout.read()
-    sts = cout.close()
+  hostmask, chanmsg = string.split(args, "PRIVMSG ", 1)
+  channelname, message = string.split(chanmsg, " :", 1)
+  if channelname[0] == "#":
+    username=channelname
+  else:
+    username, rest = string.split(hostmask, "!", 1)
+    username = username[1:]
+
+  if os.path.exists(weechat_dir + "/cryptkey." + username):
+    p = subprocess.Popen(["openssl", "enc", "-d", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+    p.stdin.write("U2FsdGVkX1" + message.replace("|","\n"))
+    p.stdin.close()
+    decrypted = p.stdout.read()
+    p.stdout.close()
     if decrypted == "":
-      return pre + ":" + middle + ":" + message
-    return pre + ":" + middle + ":" + chr(3) + "04* crypted * " + chr(15) + decrypted 
+      return args
+    return hostmask + "PRIVMSG " + channelname + " :" + chr(3) + "04* crypted * " + chr(15) + decrypted 
   else:
-    return pre + ":" + middle + ":" + message
+    return args
     
 def encrypt(server, args):
   pre, message = string.split(args, ":", 1)
   prestr=pre.split(" ")
   username=prestr[-2]
-  if os.path.exists(weechat_dir + "/cryptkey."  + server + "." + username):
-    cin, cout = os.popen2("openssl enc -a -" + CIPHER + " -pass file:" + weechat_dir + "/cryptkey." + server + "." + username + " 2>/dev/null")
-    cin.write(message)
-    cin.close()
-    encrypted = cout.read()
+  if os.path.exists(weechat_dir + "/cryptkey." + username):
+    p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+    p.stdin.write(message)
+    p.stdin.close()
+    encrypted = p.stdout.read()
+    p.stdout.close()
     encrypted = encrypted.replace("\n","|")
-    cout.close()
-    weechat.print_infobar(0,"* sent encrypted * ")
-    return pre + ":" + encrypted
+    if len(encrypted) > 400:
+      splitmsg=string.split(message," ")
+      cutpoint=len(splitmsg)/2
+      p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+      p.stdin.write(string.join(splitmsg[:cutpoint]," ") + "\n")
+      p.stdin.close()
+      encrypted = p.stdout.read()
+      p.stdout.close()
+      encrypted = encrypted.replace("\n","|")
+      p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+      p.stdin.write( string.join(splitmsg[cutpoint:]," ") )
+      p.stdin.close()
+      encrypted2 = p.stdout.read()
+      p.stdout.close()
+      encrypted2 = encrypted2.replace("\n","|")
+      encrypted = encrypted + "\n" + pre + ":" + encrypted2[10:]
+    weechat.print_infobar(0,"* sent encrypted to " + username + " * ")
+    return pre + ":" + encrypted[10:]
   else:
     weechat.remove_infobar(0)
-    return pre + ":" + message
-
+    return args
 
+# for subprocess.Popen call
+PIPE=-1
 
 # register the plugin
-weechat.register("crypt", "0.02", "", "encrypt/decrypt PRIVMSGs")
+weechat.register("crypt", version, "", "encrypt/decrypt PRIVMSGs")
 weechat_dir = weechat.get_info("weechat_dir")
 
 # register the modifiers
 weechat.add_modifier("irc_in", "privmsg", "decrypt")
 weechat.add_modifier("irc_out", "privmsg", "encrypt")
-

From 3cdf14dd451cde68c5dec85b9e43fe0575670378 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Fri, 5 Dec 2008 22:32:15 +0100
Subject: [PATCH 03/21] New version of crypt.py

---
 python/crypt.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/python/crypt.py b/python/crypt.py
index c21fa9a2..4404704c 100644
--- a/python/crypt.py
+++ b/python/crypt.py
@@ -5,7 +5,7 @@
 # License     : GPL3
 # Description : encrypt/decrypt PRIVMSGs in WeeChat using openssl
 #
-version="1.0-0.2.6"
+version="1.1-0.2.6"
 #
 # This plugin uses openssl to encrypt/decrypt messages you send
 # or receive with weechat. Due to the very simple method 
@@ -46,7 +46,7 @@ def decrypt(server, args):
     username = username[1:]
 
   if os.path.exists(weechat_dir + "/cryptkey." + username):
-    p = subprocess.Popen(["openssl", "enc", "-d", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+    p = subprocess.Popen(["openssl", "enc", "-d", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
     p.stdin.write("U2FsdGVkX1" + message.replace("|","\n"))
     p.stdin.close()
     decrypted = p.stdout.read()
@@ -62,7 +62,7 @@ def encrypt(server, args):
   prestr=pre.split(" ")
   username=prestr[-2]
   if os.path.exists(weechat_dir + "/cryptkey." + username):
-    p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+    p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
     p.stdin.write(message)
     p.stdin.close()
     encrypted = p.stdout.read()
@@ -71,13 +71,13 @@ def encrypt(server, args):
     if len(encrypted) > 400:
       splitmsg=string.split(message," ")
       cutpoint=len(splitmsg)/2
-      p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+      p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
       p.stdin.write(string.join(splitmsg[:cutpoint]," ") + "\n")
       p.stdin.close()
       encrypted = p.stdout.read()
       p.stdout.close()
       encrypted = encrypted.replace("\n","|")
-      p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, close_fds=True)
+      p = subprocess.Popen(["openssl", "enc", "-a", "-" + CIPHER, "-pass" ,"file:" + weechat_dir + "/cryptkey." + username], bufsize=4096, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
       p.stdin.write( string.join(splitmsg[cutpoint:]," ") )
       p.stdin.close()
       encrypted2 = p.stdout.read()

From f174d9c0c1e8b1f26d732361fb61ba0fe4ae749c Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Fri, 19 Dec 2008 21:25:46 +0100
Subject: [PATCH 04/21] New version of urlgrab.py

---
 python/urlgrab.py | 40 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/python/urlgrab.py b/python/urlgrab.py
index 85f49ec9..fbbe4804 100644
--- a/python/urlgrab.py
+++ b/python/urlgrab.py
@@ -1,8 +1,10 @@
 #
-# UrlGrab, version 1.2, for weechat version 0.2.4
+# UrlGrab, version 1.3 for weechat version 0.2.6
 #
 #   Listens to all channels for URLs, collects them in a list, and launches
 #   them in your favourite web server on the local host or a remote server.
+#   Copies url to X11 clipboard via xsel
+#      (http://www.vergenet.net/~conrad/software/xsel)
 #
 # Usage:
 #
@@ -82,6 +84,9 @@
 #           added parsing of scrollback buffers on load
 #    v1.2:  `historysize` was ignored
 #
+#  - With changes by ExclusivE (exclusive_tm at mail dot ru):
+#    v1.3: X11 clipboard support
+#
 # Copyright (C) 2005 Jim Ramsay <i.am@jimramsay.com>
 #
 # This program is free software; you can redistribute it and/or
@@ -106,7 +111,7 @@
 import subprocess
 
 UC_NAME="UrlGrab"
-UC_VERSION="1.2"
+UC_VERSION="1.3"
 
 def urlGrabPrint(message):
     weechat.prnt("-[%s]- %s" % ( UC_NAME, message ) )
@@ -332,6 +337,29 @@ def urlGrabCheckOnload():
             for line in reversed(lines):
                 urlGrabCheckMsgline(buf['server'], buf['channel'], line['data'])
 
+def urlGrabCopy(index):
+    global urlGrab
+
+    server = weechat.get_info("server")
+    channel = weechat.get_info("channel")
+    
+    if channel == "":
+        urlGrabPrint( "No current channel, you must activate one" )
+    elif not urlGrab.hasChannel( channel, server ):
+        urlGrabPrint("No URL found - Invalid channel")
+    else:
+        if index <= 0:
+            urlGrabPrint("No URL found - Invalid index")
+            return
+        url = urlGrab.getUrl(index, channel, server)
+        if url == "":
+            urlGrabPrint("No URL found - Invalid index")
+	else:
+	    weechat.prnt("Url: %s gone to clipboard." % url)
+	    pipe = os.popen("xsel -i","w")
+	    pipe.write(url)
+	    pipe.close()
+
 def urlGrabOpen(index, channel = None):
     global urlGrab, urlGrabSettings
 
@@ -407,6 +435,8 @@ def urlGrabHelp():
     weechat.prnt("    /url n [channel]")
     weechat.prnt("        -> launch the nth url in `/url list`")
     weechat.prnt("           or the nth url in the specified channel")
+    weechat.prnt("    /url copy [n]")
+    weechat.prnt("        -> copy nth or last url to X11 clipboard")
     weechat.prnt("")
 
 def urlGrabMain(server, args):
@@ -449,6 +479,12 @@ def urlGrabMain(server, args):
                     weechat.prnt( "  Failed: No value given" )
         except KeyError:
             weechat.prnt( "  Failed: Unrecognized parameter '%s'" % name )
+    elif largs[0] == 'copy':
+        if len(largs) > 1:
+            urlGrabCopy(int(largs[1]))
+        else:
+            urlGrabCopy(1)
+
     else:
         try:
             no = int(largs[0])

From 928a068e6cf45378aad68366411587f71d12bdab Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 5 Jan 2009 17:52:47 +0100
Subject: [PATCH 05/21] New version of oldtopic.pl: add /difftopic command

---
 perl/oldtopic.pl | 104 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/perl/oldtopic.pl b/perl/oldtopic.pl
index 013b5e95..b47cda5a 100644
--- a/perl/oldtopic.pl
+++ b/perl/oldtopic.pl
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2007 by FlashCode <flashcode@flashtux.org>
+# Copyright (c) 2007-2009 by FlashCode <flashcode@flashtux.org>
 #
 # 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
@@ -19,6 +19,8 @@
 # Display old topics for a channel.
 #
 # History:
+# 2009-01-05, Damian Viano <des@debian.org>:
+#     version 0.3: added the difftopic command
 # 2007-08-10, FlashCode <flashcode@flashtux.org>:
 #     version 0.2: upgraded licence to GPL 3
 # 2007-03-29, FlashCode <flashcode@flashtux.org>:
@@ -27,7 +29,7 @@
 
 use strict;
 
-my $version = "0.2";
+my $version = "0.3";
 
 my $plugin_help_options = "Plugins options (set with /setp):\n"
     ."  - perl.oldtopic.log_server: if on, displays all topic changes on server buffer\n"
@@ -59,6 +61,13 @@
                               "channel: channel name (default: current channel)\n\n"
                               .$plugin_help_options,
                               "%C");
+weechat::add_command_handler ("difftopic", "difftopic_cmd",
+                              "Display a diff for the last topic change for a channel",
+                              "[channel]",
+                              "channel: channel name (default: current channel)\n\n"
+                              .$plugin_help_options,
+                              "%C");
+
 
 my %oldtopic;
 undef %oldtopic;
@@ -240,3 +249,94 @@ sub oldtopic_cmd
     
     return weechat::PLUGIN_RC_OK;
 }
+
+sub difftopic_cmd
+{
+    my $server = weechat::get_info("server");
+    my $channel = weechat::get_info("channel");
+    $channel = $_[1] if ($_[1] ne "");
+    
+    if ($channel ne "")
+    {
+        my ($conf_log_server, $conf_max) = get_config();
+        
+        delete($oldtopic{$server}{$channel}) if ($conf_max == 0);
+        
+        my $count = 0;
+        my $found = 0;
+        my $tp1;
+        my $tp2;
+        
+        # get current and previous topics in tp1 and tp2
+        foreach my $date (sort { $b <=> $a } keys %{$oldtopic{$server}{$channel}})
+        {
+            $count++;
+            if ($count == 1)
+            {
+                $tp1 = $oldtopic{$server}{$channel}{$date}{"topic"};
+                $tp1 =~ s/^ +| +$//g;
+                
+            }
+            if ($count > 1)
+            {
+                $found = 1;
+                $tp2 = $oldtopic{$server}{$channel}{$date}{"topic"};
+                $tp2 =~ s/^ +| +$//g;
+                last;
+            }
+        }
+        
+        #if we have both topics, diff them
+        if ($found == 1)
+        {
+            my $diff;
+            my $i = 0;
+            my $j = 0;
+            my $k = 0;
+            
+            my @original = split /\s*\|\s*|\s+-\s+/, $tp2;
+            my @modified = split /\s*\|\s*|\s+-\s+/, $tp1;
+            
+            # this is a ripoff from irsii topic-diff.pl
+            outer: while( $i <= $#original)
+            {
+                if ($j <= $#modified && $original[$i] eq $modified[$j])
+                {
+                    $modified[$j] = '';
+                    $i += 1;
+                    $j += 1;
+                    next;
+                }
+                else
+                {
+                    # First two don't match, check the rest of the list
+                    for ($k = $j ; $k <= $#modified; $k++)
+                    {
+                        if ($modified[$k] eq $original[$i])
+                        {
+                            $modified[$k] = '';
+                            $i += 1;
+                            next outer;
+                        }
+                    }
+                    $diff = ($diff ? $diff." | " : "").$original[$i];
+                    $i += 1;
+                }
+            }
+            
+            if ($diff ne '') { weechat::print("Topic: -: ".$diff);}
+            $diff = join " | ", (grep {$_ ne ''} @modified);
+            if ($diff ne '') { weechat::print("Topic: +: ".$diff);}
+        }
+        else
+        {
+          weechat::print("No old topic for channel ${server}/${channel}.");
+        }
+    }
+    else
+    {
+        weechat::print("Error: this buffer is not a channel.");
+    }
+    
+    return weechat::PLUGIN_RC_OK;
+}

From ce9ef54bf02a4f18c2d8edb846530f44437201be Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Tue, 27 Jan 2009 12:41:19 +0100
Subject: [PATCH 06/21] Add new scripts eightball.rb and roll.rb

---
 ruby/eightball.rb |  63 ++++++++++++++++++++
 ruby/roll.rb      | 142 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 205 insertions(+)
 create mode 100644 ruby/eightball.rb
 create mode 100644 ruby/roll.rb

diff --git a/ruby/eightball.rb b/ruby/eightball.rb
new file mode 100644
index 00000000..dc3fe735
--- /dev/null
+++ b/ruby/eightball.rb
@@ -0,0 +1,63 @@
+# implement a magic 8-Ball as a weechat plugin
+# this code is licensed under the GPL v2 yadda yadda
+# contact me at: ledgekindred@gmail.com
+# latest version is always at:
+# https://code.launchpad.net/~dglidden/+junk/weechat
+
+@helptext = "Usage: /8ball question"
+@responses = ['As I see it, yes', 'Ask again later','Better not tell you now','Cannot predict now','Concentrate and ask again',
+  "Don't count on it",'It is certain','It is decidedly so','Most likely','My reply is no','My sources say no','Outlook good',
+  'Outlook not so good','Reply hazy, try again','Signs point to yes','Very doubtful','Without a doubt','Yes',
+  'Yes - definitely','You may rely on it']
+
+def weechat_init
+    Weechat.register("8ball", "1.0", "deinit", @helptext)
+    Weechat.add_command_handler("8ball", "eightball_handler", @helptext)
+    Weechat.add_message_handler("privmsg", "eightball_msg")
+    return Weechat::PLUGIN_RC_OK
+end
+
+def deinit
+    return Weechat::PLUGIN_RC_OK
+end
+
+def output(txt)
+  # Weechat.print(txt)
+  Weechat.command(txt)
+end
+
+def eightball(nick)
+  result = @responses[rand(@responses.length)]
+  response = "Magic Eight Ball tells #{nick}: #{result}"
+  return response
+end
+
+def eightball_msg(server, args)
+  if (args.empty?)
+    output(@helptext)
+    return Weechat::PLUGIN_RC_OK
+  end
+
+  null,info,msg = args.split(":",3)
+  mask,type,chan = info.split(" ")
+  nick,login = mask.split("!")
+
+  cmd = msg.split(" ")
+  if (cmd[0] == "/8ball")
+    result = eightball(nick)
+    Weechat.command(result, chan, server)
+  end
+
+  return Weechat::PLUGIN_RC_OK
+end
+
+def eightball_handler(server, args)
+  if (args.empty?)
+    output(@helptext)
+    return Weechat::PLUGIN_RC_OK
+  end
+  
+  result = eightball(Weechat.get_info("nick", server))
+  output(" /8ball #{args}")
+  output(result)
+end
diff --git a/ruby/roll.rb b/ruby/roll.rb
new file mode 100644
index 00000000..12679bdf
--- /dev/null
+++ b/ruby/roll.rb
@@ -0,0 +1,142 @@
+# weechat ruby plugin to respond to /roll command and return a 
+# randomly-generated number within the specified range
+#
+# see http://weechat.flashtux.org/doc/en/ch04s04s03.html for plugin API info
+#
+# this code is licensed under the GPL v2 yadda yadda
+# contact me at: ledgekindred@gmail.com
+# the latest version is always at:
+# https://code.launchpad.net/~dglidden/+junk/weechat
+#
+# 2009.01.14
+#   added default 1..100 if no number value is present
+#   added support for "3d6 + 1" type number format
+#
+# example syntax:
+#
+# input:              result:
+#
+# /roll                display help text
+# /roll hello          "[1d100] hello"
+# /roll 10             "[1d10]"
+# /roll 10 subway      "[1d10] subway"
+# /roll 1d6            "[1d6]"
+# /roll 2d20 to save   "[2d20] to save"
+# /roll 10 + 2         "[1d10] + 2"
+# /roll 1d6 + 2        "[1d6+2]"
+# /roll 1d6+2 to hit   "[1d6+2] to hit"
+#
+# you can use a negative modifier to the dice roll (e.g. 1d6-4)
+# if you do so, the lowest it will return is 1
+
+@helptext = "Usage: /roll (dice to roll e.g. 50 (implied 1d50), 1d6, 3d8+4, no leading integer defaults to 1d100) [text]"
+
+# weechat_init is called to set up the callback handlers
+def weechat_init
+  Weechat.register("roll", "1.0", "deinit", @helptext)
+  Weechat.add_command_handler("roll", "roll_handler", @helptext)
+  Weechat.add_message_handler("privmsg", "roll_msg")
+  return Weechat::PLUGIN_RC_OK
+end
+
+def deinit
+    return Weechat::PLUGIN_RC_OK
+end
+
+def debug(txt)
+  Weechat.print(txt)
+end
+
+def output(txt)
+  Weechat.command(txt)
+end
+
+def d(dicecount, sides)
+  roll = 0
+  for i in 1..dicecount
+    roll += rand(sides) + 1
+  end
+  
+  return roll
+end
+
+# we rolls our dice and returns our reults
+def roll(args, nick)
+  if(!args || args.empty?)
+    return @helptext
+  end
+  
+  if args =~ /^(\d+)/
+    # starts with an integer we can use to roll
+    @count = $1.to_i
+    @text = $'.strip
+    if @text =~ /^d(\d+)/
+      # we also have a type of dice to roll
+      @sides = $1.to_i
+      @text = $'.strip
+      if @text =~ /^\s*([+-])\s*(\d+)/
+        # we also have a number to add to the result
+        @add = $2.to_i
+        @roll = "d(#{@count}, #{@sides})#{$1}#{@add}"
+        @src = "#{@count}d#{@sides}#{$1}#{@add}"
+        # the rest is our text
+        @text = $'.strip
+      else
+        # we don't have a plus to the roll
+        @roll = "d(#{@count}, #{@sides})"
+        @src = "#{@count}d#{@sides}"        
+      end
+    else
+      # We don't have a type, just a number, so we go 1..@count
+      @roll = "d(1, #{@count})"
+      @src = "1d#{@count}"
+    end
+  else
+    # we didn't even start with a number, so we roll 1..100
+    @roll = "d(1, 100)"
+    @src = "1d100"
+    @text = args
+  end
+  
+  # debug("#{@roll}")
+  @val = eval @roll
+  if(@val < 1)
+    @val = 1
+  end
+  return "#{nick} rolled #{@val} (#{@src}) #{@text}"
+end
+
+# respond to messages received from server
+def roll_msg(server, args)
+  if (args.empty?)
+    output(@helptext)
+    return Weechat::PLUGIN_RC_OK
+  end
+
+  null,info,msg = args.split(":",3)
+  mask,type,chan = info.split(" ")
+  nick,login = mask.split("!")
+
+  cmd = msg.split(" ")
+  
+  # only respond to '/roll'
+  if (cmd[0] == "/roll")
+    result = roll(cmd[1..-1].join(" "), nick)
+    Weechat.command(result, chan, server)
+  end
+
+  return Weechat::PLUGIN_RC_OK
+end
+
+# respond to commands entered locally
+def roll_handler(server, args)
+  if (args.empty?)
+    output(@helptext)
+    return Weechat::PLUGIN_RC_OK
+  end
+
+  cmd = " /roll #{args}"
+  result = roll(args, Weechat.get_info("nick", server))
+  output(result)
+  output(cmd)
+end

From 83d88cac414173d012f4780d2a27d0eefba2b80a Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Sun, 5 Apr 2009 15:56:16 +0200
Subject: [PATCH 07/21] Add missing script wildcard.py

---
 python/wildcard.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 python/wildcard.py

diff --git a/python/wildcard.py b/python/wildcard.py
new file mode 100644
index 00000000..a8e06e1b
--- /dev/null
+++ b/python/wildcard.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2008 Ben <dumbtech@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.
+
+# TODO: Clean up code and add UTF-8 support.
+
+import weechat as wc
+import re
+
+wc.register("wildcard", "0.1", "", "Adds wildcard support to nick completions.")
+wc.add_keyboard_handler("wildcard")
+
+if wc.get_plugin_config("max") == "": wc.set_plugin_config("max", "9")
+max = wc.get_plugin_config("max")
+warned = False
+
+def wildcard(key, input, input_after):
+    global warned
+    if input != "":
+        pos = int(wc.get_info("input_pos"))
+        end = input.find(" ", pos)
+        if end == -1: end = None
+        word = input[input.rfind(" ", 0, pos) + 1 : end]
+        if key == "tab" and word.find("*") != -1:
+            all_nicks = wc.get_nick_info(wc.get_info("server"), wc.get_info("channel"))
+            regex = re.compile(re.escape(word).replace(r"\*", ".*"), re.I)
+            matching_nicks = filter(regex.match, all_nicks)
+            if len(matching_nicks) > int(max) and warned == False:
+                wc.prnt("Warning: you are about to expand over " + max + " nicks." \
+                        + " Press <tab> again if you're sure you want to continue.")
+                warned = True
+            else:
+                for i in range(0, len(word)): wc.command("/key call backspace")
+                wc.command("/key call insert " + ' '.join(matching_nicks))
+                warned = False
+    return wc.PLUGIN_RC_OK

From 189be7eed563cd042980ec32d3d565ea6bc796f6 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Sun, 5 Apr 2009 16:10:26 +0200
Subject: [PATCH 08/21] Update mails

---
 python/autoauth.py | 2 +-
 python/shell.py    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/python/autoauth.py b/python/autoauth.py
index 6480cd73..3c9b4e67 100644
--- a/python/autoauth.py
+++ b/python/autoauth.py
@@ -1,7 +1,7 @@
 # -*- coding: iso-8859-1 -*-
 
 # =============================================================================
-#  autoauth.py (c) October 2005 by kolter <kolter+dev@openics.org>
+#  autoauth.py (c) October 2005 by kolter <kolter@openics.org>
 #  Python script for WeeChat.
 #
 #  Licence     : GPL v2
diff --git a/python/shell.py b/python/shell.py
index 1d14b3db..c4b170dc 100644
--- a/python/shell.py
+++ b/python/shell.py
@@ -1,6 +1,6 @@
 
 # =============================================================================
-#  shell.py (c) March 2006 by Kolter <kolter+dev@openics.org>
+#  shell.py (c) March 2006 by Kolter <kolter@openics.org>
 #
 #  Licence     : GPL v2
 #  Description : running shell commands in WeeChat

From 98b8f866d42f55cb204b8958a354e556d8088950 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Wed, 15 Apr 2009 18:29:47 +0200
Subject: [PATCH 09/21] Add new script growl-net-notify.pl

---
 perl/growl-net-notify.pl | 236 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 236 insertions(+)
 create mode 100644 perl/growl-net-notify.pl

diff --git a/perl/growl-net-notify.pl b/perl/growl-net-notify.pl
new file mode 100644
index 00000000..11825f42
--- /dev/null
+++ b/perl/growl-net-notify.pl
@@ -0,0 +1,236 @@
+#
+# Copyright (c) 2009 by kinabalu (andrew AT mysticcoders DOT 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
+#
+
+#
+# Growl Notification script over network using Net::Growl
+#
+# History:
+#
+# 2009-04-10, kinabalu <andrew AT mysticcoders DOT com>
+#	version 0.1, initial version rewritten from growl-notify
+#   - original inspiration from growl-notify.pl author Zak Elep
+#
+# /growl and /gl can be used in combination with these actions
+#
+# /growl on
+# /growl off
+# /growl setup [host] [password]
+# /growl inactive [time_in_seconds]
+# /growl status
+# /growl test [message]
+# /help growl
+#
+# The script can be laoded into WeeChat by executing:
+#
+# /perl load growl-net-notify.pl
+#
+# The script may also be auto-loaded by WeeChat.  See the
+# WeeChat manual for instructions about how to do this.
+#
+# This script was tested with WeeChat version 0.2.6.  An
+# updated version of this script will be available when
+# the new WeeChat API is officially released.
+#
+# For up-to-date information about this script, and new
+# version downloads, please go to:
+#
+# http://www.mysticcoders.com/apps/growl-notify/
+#
+# If you have any questions, please contact me on-line at:
+#
+# irc.freenode.net - kinabalu (op): ##java
+#
+# - kinabalu
+#
+
+use Net::Growl;
+use Parse::IRC;
+use integer;
+
+my $growl_app = "growl-net-notify";				# name given to Growl for configuration
+my $growl_active = 1;
+
+sub message_process_init {
+	my @messages = (
+		[ weechat_highlight => 'Highlight Mentioned', '$text in $channel' ],
+		[ weechat_pv => 'Private Message', '$nick: $text' ],
+	);
+	
+	for my $message (@messages){
+		no strict 'refs';	# access symbol table
+		my $subname = join '', __PACKAGE__, '::handler_', $message->[0];
+		*{$subname} = sub
+		{
+			my($ircmsg) = parse_irc $_[1];
+			my($nick, undef) = split /!/, $ircmsg->{prefix};
+			my($channel, $text);
+			$channel = shift @{$ircmsg->{params}}
+			if $message->[0] =~ /(part|pv|highlight|topic)/;
+				$text = shift @{$ircmsg->{params}};
+	
+			my $inactivity = weechat::get_info("inactivity");
+	
+			my $message = eval qq("$message->[2]");
+	
+			if((&getc('growl_net_inactivity') - $inactivity) <= 0 && $growl_active) {
+				growl_notify( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app", "$realname" || "$nick", "$message" );
+			}	
+			return weechat::PLUGIN_RC_OK;
+		};
+		weechat::add_message_handler $message->[0], $subname;
+	}
+}
+
+#
+# smaller way to do weechat::get_plugin_config
+#
+sub getc {
+	return weechat::get_plugin_config($_[0]);	
+}
+
+
+#
+# Send notification through growl
+#
+# args: $host, $pass, $port, $application_name, $title, $description
+#
+sub growl_notify {
+	Net::Growl::notify( host=> $_[0], 
+						password=> $_[1], 
+						port=> $_[2], 
+						application=> $_[3], 
+						title=> $_[4], 
+						description => $_[5], 
+						priority=> 0, 
+						sticky=> '1' );
+}
+
+#
+# Register your app with Growl system
+#
+# args: $host, $pass, $port, $app
+#
+sub growl_register {	
+	Net::Growl::register( host=> $_[0], 
+						  password=> $_[1], 
+						  port=> $_[2], 
+						  application=> $_[3] );	
+}
+
+#
+# Handler will process commands
+#
+# /growl on
+# /growl off
+# /growl setup [host] [password] [port]
+# /growl inactive [time_in_seconds]
+# /growl status
+# /growl test [message]
+# /help growl
+#
+sub handler {
+	my $server = shift;
+	my $argList = shift;
+	
+	my @args = split(/ /, $argList);
+	my $command = @args[0];	
+		
+	if($command eq "off") {
+		$growl_active = 0;
+		weechat::print("Growl notifications: OFF");
+		return weechat::PLUGIN_RC_OK;
+	} elsif($command eq "on") {
+		$growl_active = 1;
+		weechat::print("Growl notifications: ON");
+		return weechat::PLUGIN_RC_OK;
+	} elsif($command eq "inactive") {
+		if(@args[1] >= 0) {
+			weechat::set_plugin_config("growl_net_inactivity", @args[1]);
+			weechat::print("Growl notifications inactivity set to: " . @args[1] . "s");
+			return weechat::PLUGIN_RC_OK;
+		}
+		return weechat::PLUGIN_RC_KO;	
+	} elsif($command eq "setup") {
+		if(@args[1] ne "") {
+			weechat::set_plugin_config("growl_net_client", @args[1]);			
+		} 
+		if(@args[2] ne "") {
+			weechat::set_plugin_config("growl_net_pass", @args[2]);
+		}
+		if(@args[3] ne "") {
+			weechat::set_plugin_config("growl_net_port", @args[3]);
+		}
+		growl_register( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app" );				
+		weechat::print("Growl setup re-registered with: [host: " . &getc('growl_net_client') . ":"  . &getc('growl_net_port') . ", pass: " . &getc('growl_net_pass') . "]"); 
+		return weechat::PLUGIN_RC_OK;
+	} elsif($command eq "status") {
+		weechat::print("Growl notifications: " . ($growl_active ? "ON" : "OFF") . ", inactivity timeout: " . &getc("growl_net_inactivity"));
+	} elsif($command eq "test") {
+		my $test_message = substr $argList, 5;
+		weechat::print("Sending test message: " . $test_message);
+		growl_notify( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app", "Test Message", $test_message );
+		return weechat::PLUGIN_RC_OK;
+	}
+	
+	weechat::print("Rawr!");
+}
+
+
+#
+# setup
+#
+my $version = '0.1';
+
+weechat::register $growl_app, $version, '',	'Send Weechat notifications thru Net::Growl';
+	
+weechat::add_command_handler ("growl", "handler", "setup the growl notify script",
+							  "on|off|setup [host] [password] [port]|inactive [time_in_seconds]|status|help",
+							   " on: turn on growl notifications (default)\n"
+							  ."off: turn off growl notifications\n"
+							  ."setup [host] [password] [port]: change the parameters for registration/notification with Growl\n"
+							  ."inactive [time_in_seconds]: number of seconds of inactivity before we notify (default: 30)\n"
+							  ."status: gives info on notification and inactivity settings\n"
+							  ."test [message]: send a test message\n",
+							  "on|off|setup|inactive|status");
+weechat::add_command_handler ("gl", "handler", "setup the growl notify script",
+							  "on|off|setup [host] [password] [port]|inactive [time_in_seconds]|status|help",
+							  "on: turn on growl notifications (default)\n"
+							  ."off: turn off growl notifications\n"
+							  ."setup [host] [password] [port]: change the parameters for registration/notification with Growl\n"
+							  ."inactive [time_in_seconds]: number of seconds of inactivity before we notify (default: 30)\n"
+							  ."status: gives info on notification and inactivity settings\n"
+							  ."test [message]: send a test message\n",
+							  "on|off|setup|inactive|status");
+
+my $default_growl_net_pass = "password";
+my $default_growl_net_client = "localhost";
+my $default_growl_net_inactivity = 30;
+my $default_growl_net_port = 9887;				# default UDP port used by Growl
+
+weechat::set_plugin_config("growl_net_pass", $default_growl_net_pass) if (weechat::get_plugin_config("growl_net_pass") eq "");
+weechat::set_plugin_config("growl_net_client", $default_growl_net_client) if (weechat::get_plugin_config("growl_net_client") eq "");
+weechat::set_plugin_config("growl_net_inactivity", $default_inactivity_for_message) if (weechat::get_plugin_config("growl_net_inactivity") eq "");
+weechat::set_plugin_config("growl_net_port", $default_growl_net_port) if (weechat::get_plugin_config("growl_net_port") eq "");
+		
+# register our app with growl		
+growl_register( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app" );
+
+# send up a we're here and notifying 
+growl_notify( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app", "Starting Up", "Weechat notification through Growl = on" );
+
+message_process_init();

From 6f32170e43044c3f6a98885fbbe35ff10804e56a Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Sat, 18 Apr 2009 16:57:28 +0200
Subject: [PATCH 10/21] Replace name by nick in script xmms.pl

---
 perl/xmms.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/perl/xmms.pl b/perl/xmms.pl
index a454b40b..42501712 100644
--- a/perl/xmms.pl
+++ b/perl/xmms.pl
@@ -2,7 +2,7 @@
 # xmms perl script for weechat                                      #
 #                                                                   #
 # Displays some useless xmms-infopipe values                        #
-# (c) 2006 by C�dric Chalier <ounaid AT gmail DOT com>              # 
+# (c) 2006 by linkz <ounaid AT gmail DOT com>                       #
 #                                                                   #
 # This program is free software; you can redistribute it and/or     #
 # modify it under the terms of the GNU General Public License       #

From 2ef896648e55b77492ff8f587f4a490829bf9884 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Tue, 21 Apr 2009 09:09:39 +0200
Subject: [PATCH 11/21] New version of growl-net-notify.pl: remove Parse::IRC
 dependency

---
 perl/growl-net-notify.pl | 146 ++++++++++++++++++++++-----------------
 1 file changed, 83 insertions(+), 63 deletions(-)

diff --git a/perl/growl-net-notify.pl b/perl/growl-net-notify.pl
index 11825f42..c525d637 100644
--- a/perl/growl-net-notify.pl
+++ b/perl/growl-net-notify.pl
@@ -21,12 +21,13 @@
 #
 # History:
 #
+# 2009-04-16, kinabalu <andrew AT mysticcoders DOT com>
+#	version 0.2, removed need for Parse::IRC
+#
 # 2009-04-10, kinabalu <andrew AT mysticcoders DOT com>
 #	version 0.1, initial version rewritten from growl-notify
 #   - original inspiration from growl-notify.pl author Zak Elep
 #
-# /growl and /gl can be used in combination with these actions
-#
 # /growl on
 # /growl off
 # /growl setup [host] [password]
@@ -59,50 +60,70 @@
 #
 
 use Net::Growl;
-use Parse::IRC;
 use integer;
 
 my $growl_app = "growl-net-notify";				# name given to Growl for configuration
 my $growl_active = 1;
+my $weechat_version = "0.2.6";
 
 sub message_process_init {
-	my @messages = (
-		[ weechat_highlight => 'Highlight Mentioned', '$text in $channel' ],
-		[ weechat_pv => 'Private Message', '$nick: $text' ],
-	);
-	
-	for my $message (@messages){
-		no strict 'refs';	# access symbol table
-		my $subname = join '', __PACKAGE__, '::handler_', $message->[0];
-		*{$subname} = sub
-		{
-			my($ircmsg) = parse_irc $_[1];
-			my($nick, undef) = split /!/, $ircmsg->{prefix};
-			my($channel, $text);
-			$channel = shift @{$ircmsg->{params}}
-			if $message->[0] =~ /(part|pv|highlight|topic)/;
-				$text = shift @{$ircmsg->{params}};
-	
-			my $inactivity = weechat::get_info("inactivity");
+
+	weechat::add_message_handler("weechat_highlight", "highlight_public");
+	weechat::add_message_handler("weechat_pv", "highlight_privmsg");
+}
+
+#
+# support for private messages, have to parse the IRC message
+#
+sub highlight_privmsg {
+    my ( $nick, $message ) = ( $_[1] =~ /:([^!]+)[^:]+:(.*)/ );
+    
+	send_message($nick, $message);    
+	return weechat::PLUGIN_RC_OK;	
+}
+
+#
+# support for highlights of nicks in public, have to parse the IRC message
+#
+sub highlight_public {
+	my ( $nick, $channel, $message ) = ( $_[1] =~ /:([^!]+)[^#]+([^:]+):(.*)/ );
+		
+	send_message($nick, $message . " in " . $channel); 
+	return weechat::PLUGIN_RC_OK;	
+}
+
+sub send_message {
+	my ( $nick, $message ) = @_;
 	
-			my $message = eval qq("$message->[2]");
+	my $inactivity = 0;
 	
-			if((&getc('growl_net_inactivity') - $inactivity) <= 0 && $growl_active) {
-				growl_notify( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app", "$realname" || "$nick", "$message" );
-			}	
-			return weechat::PLUGIN_RC_OK;
-		};
-		weechat::add_message_handler $message->[0], $subname;
-	}
+	$inactivity = weechat::get_info("inactivity");
+		
+	if((&getc('growl_net_inactivity') - $inactivity) <= 0 && $growl_active) {
+		growl_notify( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app", "$nick", "$message" );
+	}			
 }
 
 #
 # smaller way to do weechat::get_plugin_config
 #
 sub getc {
-	return weechat::get_plugin_config($_[0]);	
+	return weechat::get_plugin_config($_[0]);
+}
+
+#
+# smaller way to do weechat::get_plugin_config
+#
+sub setc {
+	return weechat::set_plugin_config($_[0], $_[1]);
 }
 
+#
+# print function
+# 
+sub prt {
+	weechat::print($_[0]);
+}
 
 #
 # Send notification through growl
@@ -144,60 +165,68 @@ sub growl_register {
 # /help growl
 #
 sub handler {
+	no strict 'refs';	# access symbol table
+	
 	my $server = shift;
 	my $argList = shift;
-	
+
 	my @args = split(/ /, $argList);
-	my $command = @args[0];	
+	my $command = $args[0];	
 		
+	if(!$command) {
+		prt("Rawr!");
+		return weechat::PLUGIN_RC_OK;
+	}
+	
 	if($command eq "off") {
 		$growl_active = 0;
-		weechat::print("Growl notifications: OFF");
+		prt("Growl notifications: OFF");
 		return weechat::PLUGIN_RC_OK;
 	} elsif($command eq "on") {
 		$growl_active = 1;
-		weechat::print("Growl notifications: ON");
+		prt("Growl notifications: ON");
 		return weechat::PLUGIN_RC_OK;
 	} elsif($command eq "inactive") {
-		if(@args[1] >= 0) {
-			weechat::set_plugin_config("growl_net_inactivity", @args[1]);
-			weechat::print("Growl notifications inactivity set to: " . @args[1] . "s");
+		if(exists $args[1] && $args[1] >= 0) {
+			setc("growl_net_inactivity", $args[1]);
+			prt("Growl notifications inactivity set to: " . $args[1] . "s");
 			return weechat::PLUGIN_RC_OK;
 		}
 		return weechat::PLUGIN_RC_KO;	
 	} elsif($command eq "setup") {
-		if(@args[1] ne "") {
-			weechat::set_plugin_config("growl_net_client", @args[1]);			
+		if(exists $args[1] && $args[1] ne "") {
+			setc("growl_net_client", $args[1]);			
 		} 
-		if(@args[2] ne "") {
-			weechat::set_plugin_config("growl_net_pass", @args[2]);
+		if(exists $args[2] && $args[2] ne "") {
+			setc("growl_net_pass", $args[2]);
 		}
-		if(@args[3] ne "") {
-			weechat::set_plugin_config("growl_net_port", @args[3]);
+		if(exists $args[3] && $args[3] ne "") {
+			setc("growl_net_port", $args[3]);
 		}
 		growl_register( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app" );				
-		weechat::print("Growl setup re-registered with: [host: " . &getc('growl_net_client') . ":"  . &getc('growl_net_port') . ", pass: " . &getc('growl_net_pass') . "]"); 
+		prt("Growl setup re-registered with: [host: " . &getc('growl_net_client') . ":"  . &getc('growl_net_port') . ", pass: " . &getc('growl_net_pass') . "]"); 
 		return weechat::PLUGIN_RC_OK;
 	} elsif($command eq "status") {
-		weechat::print("Growl notifications: " . ($growl_active ? "ON" : "OFF") . ", inactivity timeout: " . &getc("growl_net_inactivity"));
+		prt("Growl notifications: " . ($growl_active ? "ON" : "OFF") . ", inactivity timeout: " . &getc("growl_net_inactivity"));
+		return weechat::PLUGIN_RC_OK;
 	} elsif($command eq "test") {
 		my $test_message = substr $argList, 5;
-		weechat::print("Sending test message: " . $test_message);
+		prt("Sending test message: " . $test_message);
 		growl_notify( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app", "Test Message", $test_message );
 		return weechat::PLUGIN_RC_OK;
 	}
-	
-	weechat::print("Rawr!");
+
+    return weechat::PLUGIN_RC_KO;
 }
 
 
 #
 # setup
 #
-my $version = '0.1';
+my $version = '0.2';
 
 weechat::register $growl_app, $version, '',	'Send Weechat notifications thru Net::Growl';
-	
+		
 weechat::add_command_handler ("growl", "handler", "setup the growl notify script",
 							  "on|off|setup [host] [password] [port]|inactive [time_in_seconds]|status|help",
 							   " on: turn on growl notifications (default)\n"
@@ -207,25 +236,16 @@ sub handler {
 							  ."status: gives info on notification and inactivity settings\n"
 							  ."test [message]: send a test message\n",
 							  "on|off|setup|inactive|status");
-weechat::add_command_handler ("gl", "handler", "setup the growl notify script",
-							  "on|off|setup [host] [password] [port]|inactive [time_in_seconds]|status|help",
-							  "on: turn on growl notifications (default)\n"
-							  ."off: turn off growl notifications\n"
-							  ."setup [host] [password] [port]: change the parameters for registration/notification with Growl\n"
-							  ."inactive [time_in_seconds]: number of seconds of inactivity before we notify (default: 30)\n"
-							  ."status: gives info on notification and inactivity settings\n"
-							  ."test [message]: send a test message\n",
-							  "on|off|setup|inactive|status");
 
 my $default_growl_net_pass = "password";
 my $default_growl_net_client = "localhost";
 my $default_growl_net_inactivity = 30;
 my $default_growl_net_port = 9887;				# default UDP port used by Growl
 
-weechat::set_plugin_config("growl_net_pass", $default_growl_net_pass) if (weechat::get_plugin_config("growl_net_pass") eq "");
-weechat::set_plugin_config("growl_net_client", $default_growl_net_client) if (weechat::get_plugin_config("growl_net_client") eq "");
-weechat::set_plugin_config("growl_net_inactivity", $default_inactivity_for_message) if (weechat::get_plugin_config("growl_net_inactivity") eq "");
-weechat::set_plugin_config("growl_net_port", $default_growl_net_port) if (weechat::get_plugin_config("growl_net_port") eq "");
+&setc("growl_net_pass", $default_growl_net_pass) if (&getc("growl_net_pass") eq "");
+&setc("growl_net_client", $default_growl_net_client) if (&getc("growl_net_client") eq "");
+&setc("growl_net_inactivity", $default_growl_net_inactivity) if (&getc("growl_net_inactivity") eq "");
+&setc("growl_net_port", $default_growl_net_port) if (&getc("growl_net_port") eq "");
 		
 # register our app with growl		
 growl_register( &getc('growl_net_client'), &getc('growl_net_pass'), &getc('growl_net_port'), "$growl_app" );

From 9d00da1fc2fe1b0398fa32c93fe2eb7c79bf6471 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Sat, 16 May 2009 10:32:56 +0200
Subject: [PATCH 12/21] Add new script remnotify.pl

---
 perl/remnotify.pl | 59 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 perl/remnotify.pl

diff --git a/perl/remnotify.pl b/perl/remnotify.pl
new file mode 100644
index 00000000..60e33dfc
--- /dev/null
+++ b/perl/remnotify.pl
@@ -0,0 +1,59 @@
+#
+# Copyright (c) 2009 by Oleg Melnik <boten@blindage.org>
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+#
+# When you are got highlighted or private message, this script send a notification to remote address.
+# This script sends simple data via a socket (_HEADER_ (nick_of_slapper) slapped on (channel) _END_HEADER_ _TEXT_ (text_of_message) _END_TEXT_)
+# You can easily use your own program on remote machine to receive and show notifications. For example, download script which shows
+# these message this libnotify: http://boten.blindage.org/?attachment_id=433
+use IO::Socket;
+
+my $version = "0.1";
+
+#Default values of address of remote server
+my $defaddress = "localhost";
+my $defport = "10020";
+
+#Register script
+weechat::register("remnotify", $version, "", "Sends notification about highlighted and private msg to remote address");
+
+#Set default settings on first register
+weechat::set_plugin_config("remaddress", $defaddress) if (weechat::get_plugin_config("remaddress") eq "");
+weechat::set_plugin_config("remport", $defport) if (weechat::get_plugin_config("remport") eq "");
+
+#Bind events to our functions
+weechat::add_message_handler("weechat_highlight", "on_highlight");
+weechat::add_message_handler("weechat_pv", "on_pv");
+
+sub on_highlight {
+	my $message = $_[1];
+	my $nick = $+ if $message =~ /([^:]+)!/;
+	my $channel = $+ if $message =~ /PRIVMSG\s(#*\S+)\s.+/;
+	my $text = $+ if $message =~ /PRIVMSG\s#*.+\s:(.+)$/;
+	my $remaddress = weechat::get_plugin_config("remaddress");
+	my $remport = weechat::get_plugin_config("remport");
+	my $nsocket = IO::Socket::INET->new(Type=>SOCK_STREAM, Proto=>'tcp', PeerAddr => $remaddress, PeerPort => $remport)
+	or die "Couldn't connect to remove server: $@";
+	my $msg = "_HEADER_ $nick wrote to $channel _END_HEADER_ _TEXT_ $text _END_TEXT_";
+	print $nsocket "$msg\n";
+	close $nsocket;
+	return weechat::PLUGIN_RC_OK;
+}
+sub on_pv {
+	my $message = $_[1];
+	on_highlight(" ", $message);
+	return weechat::PLUGIN_RC_OK;
+}
\ No newline at end of file

From b1d5747e15db8aa15e32a5619b1c50b4d6a014cf Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 8 Jun 2009 12:13:18 +0200
Subject: [PATCH 13/21] Add new script weebanshee.py (for v0.2.6)

---
 python/weebanshee.py | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 python/weebanshee.py

diff --git a/python/weebanshee.py b/python/weebanshee.py
new file mode 100644
index 00000000..4a906421
--- /dev/null
+++ b/python/weebanshee.py
@@ -0,0 +1,28 @@
+# Weechat now-playing script for Banshee (tested for 1.4.3)
+# Author: Vitaly Dolgov [ ferhiord@gmail.com ]
+# Usage: /weebanshee
+
+import os
+import weechat
+
+name = 'weebanshee'
+version = '0.1'
+banshee = 'banshee-1'
+
+weechat.register(name, version, '', 
+    'now-playing script for banshee (usage: /%s)' % name)
+weechat.add_command_handler(name, 'show')
+
+def show(server, args) :
+  if os.popen('ps -e | grep %s' % banshee).read() == '' :
+    return weechat.PLUGIN_RC_KO
+  if os.popen('%s --query-current-state' % banshee
+      ).read().strip().split(' ')[1] == 'idle' :
+    return weechat.PLUGIN_RC_KO
+  artist = os.popen('%s --query-artist' % banshee
+      ).read().strip().split(' ', 1)[1]
+  title  = os.popen('%s --query-title'  % banshee
+      ).read().strip().split(' ', 1)[1]
+  text = '/me : %s - %s' % (artist, title)
+  weechat.command(text)
+  return weechat.PLUGIN_RC_OK

From de69c5402fa3f468cda251e516f229568dd85eec Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 8 Jun 2009 12:15:24 +0200
Subject: [PATCH 14/21] Add license in script weebanshee.py

---
 python/weebanshee.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/python/weebanshee.py b/python/weebanshee.py
index 4a906421..9fee3500 100644
--- a/python/weebanshee.py
+++ b/python/weebanshee.py
@@ -1,6 +1,7 @@
 # Weechat now-playing script for Banshee (tested for 1.4.3)
 # Author: Vitaly Dolgov [ ferhiord@gmail.com ]
 # Usage: /weebanshee
+# Released under GPLv2
 
 import os
 import weechat

From c4f56dbb7c66be8f34ead37fa20ba8aff1cc0ad8 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 8 Jun 2009 15:01:22 +0200
Subject: [PATCH 15/21] New version of weexaile.py

---
 python/weexaile.py | 60 ++++++++++++----------------------------------
 1 file changed, 15 insertions(+), 45 deletions(-)

diff --git a/python/weexaile.py b/python/weexaile.py
index bfab1f37..cb784e6b 100644
--- a/python/weexaile.py
+++ b/python/weexaile.py
@@ -1,53 +1,23 @@
-#Author: Pablo Escobar <pablo__escobar AT tlen DOT pl>
-#What it does: This script shows the currently played song in exaile 
-#Usage: /weexaile - Displays the songname
-#Released under GNU GPL v2 or newer
-
-#/usr/bin/python
-#coding: utf-8
+# Authors: Pablo Escobar <pablo__escobar AT tlen DOT pl>
+#          Vitaly Dolgov <ferhiord AT gmail DOT com>
+# What it does: this script shows the currently played song in exaile. 
+# Usage: /weexaile - displays the songname
+# Released under GPLv2 or newer
 
 import weechat
 import re
 import codecs
 from os import popen
 
-weechat.register ('exaile', '0.01', '', """exaile-weechat current song script (usage: /weexaile)""")
-weechat.add_command_handler ('weexaile', 'show_it_to_them')
-
-default = {
-  "msg_head": "is playing",
-  "msg_tail": "with exaile",
-  "spacer": "★",
-  "colour_artist": "C03",
-  "colour_title": "C02",
-  "colour_lenght": "C05",
-  "colour_spacer": "C08",
-}
-
-for k, v in default.items():
-  if not weechat.get_plugin_config(k):
-    weechat.set_plugin_config(k, v)
+weechat.register('exaile', '0.02', '', 'exaile-weechat current song script (usage: /weexaile)')
+weechat.add_command_handler('weexaile', 'show_it_to_them')
 
 def show_it_to_them(server, args):
-	spacer = weechat.get_plugin_config("spacer")
-	msg_tail = weechat.get_plugin_config("msg_tail")
-	msg_head = weechat.get_plugin_config("msg_head")
-	colour_artist = weechat.get_plugin_config("colour_artist")
-	colour_title = weechat.get_plugin_config("colour_title")
-	colour_lenght = weechat.get_plugin_config("colour_lenght")
-	colour_spacer = weechat.get_plugin_config("colour_spacer")
-	exaile_running = popen ('exaile --get-title')
-	exaile_running_text = exaile_running.readline().rstrip()
-	if exaile_running_text != "No running Exaile instance found.":
-		song_name = popen ('exaile --get-title')
-		song_name_text = song_name.readline().rstrip()
-		song_artist = popen ('exaile --get-artist')
-		song_artist_text = song_artist.readline().rstrip()
-		song_length = popen ('exaile --get-length')
-		song_length_text = song_length.readline().rstrip()
-		song_current = popen ('exaile --current-position')
-		song_current_text = str(round(float(song_current.readline().rstrip()),2))
-		all = '/me ' + msg_head + ' %' + colour_title + song_name_text + ' %' + colour_spacer + spacer + ' %' + colour_artist + song_artist_text + ' %' + colour_spacer + spacer + ' %' + colour_lenght + song_length_text + " (" + song_current_text + "%)" + " %C00" + msg_tail 
-		weechat.command(all)
-	return 0
-
+  to_devnull = ' 2> /dev/null'
+  exaile_running = popen('exaile --get-title' + to_devnull).readline().rstrip()
+  if exaile_running != "No running Exaile instance found." :
+    song_name = popen('exaile --get-title' + to_devnull).readline().rstrip()
+    song_artist = popen('exaile --get-artist' + to_devnull).readline().rstrip()
+    text = '/me : ' + song_artist + ' - ' + song_name
+    weechat.command(text)
+  return weechat.PLUGIN_RC_OK

From c76d56a083e4a6a434648a46ac790ddb27d8b484 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Sun, 5 Jul 2009 09:37:08 +0200
Subject: [PATCH 16/21] Add new script wetwit.py: send/receive twitter messages

---
 python/wetwit.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 python/wetwit.py

diff --git a/python/wetwit.py b/python/wetwit.py
new file mode 100644
index 00000000..fbad15f4
--- /dev/null
+++ b/python/wetwit.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+#
+# Author: Stefano Zamprogno <mie.iscrizioni@gmail.com>
+# Licence: GPL3
+#
+# This plugin need:
+# python (tested only with v2.6.x)
+# python-twitter (tested only with v0.6)
+# weechat (tested only with v0.2.6.3)
+# on Archlinux distro simply:
+# yaourt -S python-twitter
+#
+# Put wetwit.py on ~/.weechat/python/autoload/
+# set below user and pwd
+#
+# HELP on Usage
+# /twit twits [N]
+#       request latest [N] messages or latest 5 if N not specified
+# /twit sendmsg text2send
+#       send 'text2send' to twitter
+
+import weechat
+import twitter
+
+weechat.register("wetwit.py", "0.1.0", "",
+                            "WeTwit v.0.1.0")
+
+
+# To be SET by you, VERIFY 3 TIMES !!! :)
+user = "john"
+pwd = "doe"
+
+# -----------------------------------------------------------
+# Do not modify below this line------------------------------
+# -----------------------------------------------------------
+
+api = twitter.Api(user, pwd)
+
+def hook_commands_cb(server, args):
+    cmd = args.split()
+    if len(cmd) > 1:
+        cmd,txt = args.split(" ", 1)
+        cmd = cmd.lower()
+        if cmd == "sendmsg":
+            weechat.prnt("Working...")
+            if len(txt) <= 140:
+                status = api.PostUpdate(txt.decode("utf-8"))
+                weechat.prnt("-"*30)
+                weechat.prnt(status.text.encode("utf-8"))
+                weechat.prnt("-"*30)
+                weechat.prnt("*** TWITTED ***")
+            else:
+                statuses = api.PostUpdates(txt.decode("utf-8"),
+                                        continuation="...")
+                for s in statuses:
+                    weechat.prnt("-"*30)
+                    weechat.prnt(s.text.encode('utf-8'))
+                    weechat.prnt("-"*30)
+                    weechat.prnt("*** TWITTED ***")
+        if cmd == "twits":
+            weechat.prnt("Working...")
+            statuses = api.GetFriendsTimeline(count=str(txt))
+            for s in statuses:
+                weechat.prnt("Data: %s" % s.created_at)
+                weechat.prnt("ID: %s, User: %s" % (s.id, s.user.name))
+                weechat.prnt("Text: %s" % (s.text.encode('utf-8')))
+                weechat.prnt("---")
+    elif len(cmd) == 1:
+        if cmd[0].lower() == "twits":
+            weechat.prnt("Working...")
+            statuses = api.GetFriendsTimeline(count=5)
+            for s in statuses:
+                weechat.prnt("Data: %s" % s.created_at)
+                weechat.prnt("ID: %s, User: %s" % (s.id, s.user.name))
+                weechat.prnt("Text: %s" % (s.text.encode('utf-8')))
+                weechat.prnt("---")
+
+    return weechat.PLUGIN_RC_OK
+
+weechat.add_command_handler("twit", "hook_commands_cb",
+                        "Send/Receive twitter messages",
+                        "")
+
+
+weechat.prnt("Plugin WeTwit loaded!")

From 09a71fc1683e9103b98a7842c6a0f040d1f3bbcf Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 10 Aug 2009 12:26:58 +0200
Subject: [PATCH 17/21] Add new script amarok2.py (for v0.2.6)

---
 python/amarok2.py | 267 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 267 insertions(+)
 create mode 100644 python/amarok2.py

diff --git a/python/amarok2.py b/python/amarok2.py
new file mode 100644
index 00000000..8ad34d5d
--- /dev/null
+++ b/python/amarok2.py
@@ -0,0 +1,267 @@
+#
+# Copyright (c) 2009 by Eric Gach <eric.gach@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
+#
+
+import weechat
+import re
+import os
+import subprocess
+import traceback
+
+__desc__ = 'Amarok2 control and now playing script for Weechat.'
+__version__ = '1.0.0'
+__author__ = 'Eric Gach <eric.gach@gmail.com>'
+
+debug = {}
+infobar = {}
+output = {}
+ssh = {'enabled': False}
+
+STATUS_PLAYING = 0
+STATUS_PAUSED = 1
+STATUS_STOPPED = 2
+
+class amarok2_exception(Exception):
+	pass
+
+def amarok2_command(server, args):
+    try:
+        args = args.split(' ')
+        if args[0] == 'infobar':
+            if infobar['enabled']:
+                infobar['enabled'] = False
+                weechat.set_plugin_config('infobar_enabled', '0')
+                weechat.remove_timer_handler('amarok2_infobar_update')
+                weechat.remove_infobar(0)
+                weechat.prnt('Infobar disabled')
+            else:
+                infobar['enabled'] = True
+                weechat.set_plugin_config('infobar_enabled', '1')
+                amarok2_infobar_update()
+                weechat.add_timer_handler(infobar['update'], 'amarok2_infobar_update')
+                weechat.prnt('Amarok2 infobar enabled')
+            return weechat.PLUGIN_RC_OK
+        elif args[0] == 'next':
+			if _get_status() == STATUS_STOPPED:
+				weechat.prnt('Amarok2: Not playing, cannot go to next song.')
+				return weechat.PLUGIN_RC_KO
+			else:
+				_execute_command(_dbus_command('Next'))
+				weechat.prnt('Amarok2: Playing next song.')
+				return weechat.PLUGIN_RC_OK
+        elif args[0] == 'np':
+            return amarok2_now_playing(server)
+        elif args[0] == 'pause':
+			if _get_status() == STATUS_PAUSED:
+				weechat.prnt('Amarok2: Already paused')
+				return weechat.PLUGIN_RC_KO
+			else:
+				_execute_command(_dbus_command('Pause'))
+				weechat.prnt('Amarok2: Song paused.')
+				return weechat.PLUGIN_RC_OK
+        elif args[0] == 'play':
+			if _get_status() == STATUS_PLAYING:
+				weechat.prnt('Amarok2: Already playing')
+				return weechat.PLUGIN_RC_KO
+			else:
+				_execute_command(_dbus_command('Play'))
+				weechat.prnt('Amarok2: Started playing.')
+				return weechat.PLUGIN_RC_OK
+        elif args[0] == 'prev':
+			if _get_status() == STATUS_STOPPED:
+				weechat.prnt('Amarok2: Not playing, cannot go to previous song.')
+				return weechat.PLUGIN_RC_KO
+			else:
+				_execute_command(_dbus_command('Prev'))
+				weechat.prnt('Amarok2: Playing previous song.')
+				return weechat.PLUGIN_RC_OK
+        elif args[0] == 'stop':
+            _execute_command(_dbus_command('Stop'))
+            weechat.prnt('Amarok2: Stop playing.')
+            return weechat.PLUGIN_RC_OK
+        elif args[0] == '':
+            return amarok2_display_help(server)
+        else:
+            weechat.prnt('Amarok2: Unknown command %s' % (args[0]), '', server)
+            return weechat.PLUGIN_RC_OK
+    except amarok2_exception, ex:
+        return weechat.PLUGIN_RC_KO
+    except:
+        file = open(debug['file'], 'w')
+        traceback.print_exc(None, file)
+        weechat.prnt('Unknown Exception encountered. Stack dumped to %s' % (debug['file']), '', server)
+        return weechat.PLUGIN_RC_KO
+
+def amarok2_display_help(server):
+	weechat.prnt('%s - Version: %s' % (__desc__, __version__), '', server)
+	weechat.prnt('Author: %s' % (__author__), '', server)
+	weechat.prnt('', '', server)
+	weechat.prnt('Commands Available', '', server)
+	weechat.prnt('  /amarok2 next    - Move to the next song in the playlist.', '', server)
+	weechat.prnt('  /amarok2 np      - Display currently playing song.', '', server)
+	weechat.prnt('  /amarok2 play    - Start playing music.', '', server)
+	weechat.prnt('  /amarok2 pause   - Toggle between pause/playing.', '', server)
+	weechat.prnt('  /amarok2 prev    - Move to the previous song in the playlist.', '', server)
+	weechat.prnt('  /amarok2 stop    - Stop playing music.', '', server)
+	weechat.prnt('  /amarok2 infobar - Toggle the infobar display.', '', server)
+	weechat.prnt('', '', server)
+	weechat.prnt('Formatting', '', server)
+	weechat.prnt('  %artist%    - Replaced with the song artist.', '', server)
+	weechat.prnt('  %title%     - Replaced with the song title.', '', server)
+	weechat.prnt('  %album%     - Replaced with the song album.', '', server)
+	weechat.prnt('  %year%      - Replaced with the song year tag.', '', server)
+	weechat.prnt('  %cTime%     - Replaced with how long the song has been playing.', '', server)
+	weechat.prnt('  %tTime%     - Replaced with the length of the song.', '', server)
+	weechat.prnt('  %bitrate%   - Replaced with the bitrate of the song.', '', server)
+	weechat.prnt('  %C##        - Make ## the number code of the color you want to use. Use %C by itself to end the color.', '', server)
+	weechat.prnt('', '', server)
+	weechat.prnt('To see all available settings, please check /setp amarok2')
+	return weechat.PLUGIN_RC_OK
+
+def amarok2_infobar_update():
+	_load_settings()
+	if infobar['enabled'] == False:
+		return weechat.PLUGIN_RC_OK
+
+	if _get_status() == STATUS_STOPPED:
+		weechat.print_infobar(infobar['update'], 'Amarok is not currently playing')
+		return weechat.PLUGIN_RC_OK
+	else:
+		song = _get_song_info()
+		format = _format_np(infobar['format'], song)
+		weechat.print_infobar(infobar['update'], format)
+		return weechat.PLUGIN_RC_OK
+
+def amarok2_now_playing(server):
+	_load_settings()
+	if _get_status() == STATUS_STOPPED:
+		weechat.prnt('Amarok is not playing.', '', server)
+		return weechat.PLUGIN_RC_KO
+	else:
+		song = _get_song_info()
+		format = _format_np(output['format'], song)
+		weechat.command(format)
+		return weechat.PLUGIN_RC_OK
+
+def amarok2_unload():
+	"""Unload the plugin from weechat"""
+	if infobar['enabled']:
+		weechat.remove_infobar(0)
+		weechat.remove_timer_handler('amarokInfobarUpdate')
+	return weechat.PLUGIN_RC_OK
+
+def _dbus_command(command):
+	# we still have to use dbus-send because qdbus doesn't support complex types
+	# yet. the GetStatus is one that isn't supported by qdbus yet.
+	return 'DISPLAY=":0.0" dbus-send --type=method_call --print-reply --dest=org.kde.amarok /Player org.freedesktop.MediaPlayer.%s' % (command,)
+
+def _execute_command(cmd):
+	from subprocess import PIPE
+	if ssh['enabled']:
+		cmd = 'ssh -p %d %s@%s "%s"' % (ssh['port'], ssh['user'], ssh['host'], cmd)
+	proc = subprocess.Popen(cmd, shell = True, stderr = PIPE, stdout = PIPE, close_fds = True)
+	error = proc.stderr.read()
+	if error != '':
+		weechat.prnt(error)
+	output = proc.stdout.read()
+	proc.wait()
+	return output
+
+def _format_np(template, song):
+	np = template.replace('%artist%', song['artist'])
+	np = np.replace('%title%', song['title'])
+	np = np.replace('%album%', song['album'])
+	np = np.replace('%cTime%', song['cTime'])
+	np = np.replace('%tTime%', song['time'])
+	np = np.replace('%bitrate%', song['audio-bitrate'])
+	np = np.replace('%year%', song['year'])
+	np = np.replace('%C', chr(3))
+	if _get_status() == STATUS_PAUSED:
+		np = np + " - [PAUSED]"
+	return np
+
+def _format_seconds(s):
+	# seconds should include milliseconds
+	s = int(s) / 1000
+	temp = float()
+	temp = float(s) / (60*60*24)
+	d = int(temp)
+	temp = (temp - d) * 24
+	h = int(temp)
+	temp = (temp - h) * 60
+	m = int(temp)
+	temp = (temp - m) * 60
+	sec = temp
+	if d > 0:
+		return "%id %i:%02i:%02i" % (d, h, m, sec)
+	elif h > 0:
+		return "%i:%02i:%02i" % (h, m, sec)
+	else:
+		return "%i:%02i" % (m, sec)
+
+def _get_song_info():
+	"""Get the song information from amarok"""
+	song = {}
+	info = _execute_command(_dbus_command('GetMetadata'))
+	matches = re.findall('dict\s+entry\(\s+string\s+"([^"]+)"\s+variant\s+([a-z0-9]+)\s+([^\n]*)\s+\)', info)
+	for x in matches:
+		if x[1] == 'string':
+			# Remove the quotes from strings
+			song[x[0]] = x[2].strip('"')
+		else:
+			song[x[0]] = x[2]
+	song['time'] = _format_seconds(song['mtime'])
+	song['cTime'] = _format_seconds(re.findall('int32 ([0-9]+)', _execute_command(_dbus_command('PositionGet')))[0])
+	return song
+
+def _get_status():
+	status = _execute_command(_dbus_command('GetStatus'))
+	matches = re.findall('int32 ([0-9])', status)
+	# first one is our playing status - 0 = playing, 1 = paused, 2 = stopped
+	return int(matches[0])
+
+def _load_settings():
+    debug['file'] = os.path.expanduser(_load_setting('debug_file', '~/amarok2_debug.txt'))
+    infobar['enabled'] = _load_setting('infobar_enabled', '0', 'bool')
+    infobar['format'] = _load_setting('infobar_format', 'Now Playing: %title% by %artist%')
+    infobar['update'] = _load_setting('infobar_update', '10', 'int')
+    output['format'] = _load_setting('output_format', '/me is listening to %C04%title%%C by %C03%artist%%C from %C12%album%%C [%cTime% of %tTime% @ %bitrate%kbps]')
+    ssh['enabled'] = _load_setting('ssh_enabled', '0', 'bool')
+    ssh['host'] = _load_setting('ssh_host', 'localhost')
+    ssh['port'] = _load_setting('ssh_port', '22', 'int')
+    ssh['user'] = _load_setting('ssh_user', 'user')
+
+def _load_setting(setting, default=None, type=None):
+	value = weechat.get_plugin_config(setting)
+	if value == '' and default != None:
+		weechat.set_plugin_config(setting, default)
+		value = default
+
+	if type == 'int' or type == 'bool':
+		value = int(value)
+
+	if type == 'bool':
+		value = bool(value)
+
+	return value
+
+if weechat.register('amarok2', __version__, 'amarok2_unload', __desc__):
+	_load_settings()
+	if infobar['enabled']:
+		amarok2_infobar_update()
+		weechat.add_timer_handler(infobar['update'], 'amarok2_infobar_update')
+	weechat.add_command_handler('amarok2', 'amarok2_command', 'Control Amarok2 or display now playing information.', 'next|np|play|pause|prev|stop|infobar')

From 5777b5e12b3ff76aca4febcfbbf70131077fdd02 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Wed, 19 Aug 2009 14:13:40 +0200
Subject: [PATCH 18/21] Add new script rainbow.py (for v0.2.6)

---
 python/rainbow.py | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 python/rainbow.py

diff --git a/python/rainbow.py b/python/rainbow.py
new file mode 100644
index 00000000..fe435d2c
--- /dev/null
+++ b/python/rainbow.py
@@ -0,0 +1,26 @@
+#Author: Martin Pugh
+#Contact: mpugh89@gmail.com
+#Usage: /rainbow some text here
+#Displays text in rainbow colours
+#License: WTFPL v2
+
+import weechat
+weechat.register('rainbow', '0.5', '', """Print rainbow-colored text. Usage: /rainbow""")
+weechat.add_command_handler("rainbow", "rainbow", "print rainbow text")
+
+def rainbow(server, args):
+	colors=["9","11","12","13","4"]
+	out = ""
+	ci = 0
+	if args != "":
+		for i in range(0,len(args)):
+			out += '\x03' + colors[ci] + '\x02' + args[i]
+			if ci == len(colors)-1:
+				ci = 0
+			else:
+				ci += 1
+		outcommand = '/say ' + out
+		weechat.command(outcommand)
+	else:
+		weechat.prnt("rainbow: no text to output")
+	return weechat.PLUGIN_RC_OK

From f25bca9e976ab350d203f941b3afe51e3df172dc Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Mon, 31 Aug 2009 12:56:01 +0200
Subject: [PATCH 19/21] Add new script moc-control.py (for v0.2.6)

---
 python/moc-control.py | 271 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 271 insertions(+)
 create mode 100644 python/moc-control.py

diff --git a/python/moc-control.py b/python/moc-control.py
new file mode 100644
index 00000000..90f3fefb
--- /dev/null
+++ b/python/moc-control.py
@@ -0,0 +1,271 @@
+#
+# Copyright (c) 2009 by Benjamin Neff <info@benjaminneff.ch>
+#
+# 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
+#
+
+import weechat
+import os
+import subprocess
+import traceback
+
+infobar = {}
+output = {}
+
+STATUS_PLAYING = 'PLAY'
+STATUS_PAUSED = 'PAUSE'
+STATUS_STOPPED = 'STOP'
+
+def moc_command(server, args):
+	args = args.split(' ')
+	if args[0] == '' or args[0] == 'i' or args[0] == 'o' or args[0] == 'ot':
+		return moc_now_playing(server, args[0])
+	elif args[0] == 'pause' or args[0] == 'pp':
+		if _get_status() == STATUS_STOPPED:
+			weechat.prnt('moc: Not playing')
+			return weechat.PLUGIN_RC_OK
+		else:
+			_execute_command('mocp -G')
+			weechat.prnt('moc: Song paused / Continue playing')
+			return weechat.PLUGIN_RC_OK
+	elif args[0] == 'play':
+		if _get_status() == STATUS_PLAYING:
+			weechat.prnt('moc: Already playing')
+			return weechat.PLUGIN_RC_OK
+		elif _get_status() == STATUS_PAUSED:
+			_execute_command('mocp -U')
+			weechat.prnt('moc: Continue playing.')
+			return weechat.PLUGIN_RC_OK
+		else:
+			_execute_command('mocp -p')
+			weechat.prnt('moc: Started playing.')
+			return weechat.PLUGIN_RC_OK
+	elif args[0] == 'stop':
+		_execute_command('mocp -s')
+		weechat.prnt('moc: Stop playing.')
+		return weechat.PLUGIN_RC_OK
+	elif args[0] == 'prev':
+		if _get_status() == STATUS_STOPPED:
+			weechat.prnt('moc: Not playing, cannot go to previous song.')
+			return weechat.PLUGIN_RC_OK
+		else:
+			_execute_command('mocp -r')
+			weechat.prnt('moc: Playing previous song.')
+			return weechat.PLUGIN_RC_OK
+	elif args[0] == 'next':
+		if _get_status() == STATUS_STOPPED:
+			weechat.prnt('moc: Not playing, cannot go to next song.')
+			return weechat.PLUGIN_RC_OK
+		else:
+			_execute_command('mocp -f')
+			weechat.prnt('moc: Playing next song.')
+			return weechat.PLUGIN_RC_OK
+	elif args[0] == 'infobar':
+		if infobar['enabled']:
+			infobar['enabled'] = False
+			weechat.set_plugin_config('infobar_enabled', '0')
+			weechat.remove_timer_handler('moc_infobar_update')
+			weechat.remove_infobar(0)
+			weechat.prnt('moc infobar disabled')
+		else:
+			infobar['enabled'] = True
+			weechat.set_plugin_config('infobar_enabled', '1')
+			moc_infobar_update()
+			weechat.add_timer_handler(infobar['update'], 'moc_infobar_update')
+			weechat.prnt('moc infobar enabled')
+		return weechat.PLUGIN_RC_OK
+	elif args[0] == 'help':
+		weechat.command('/help moc')
+		return weechat.PLUGIN_RC_OK
+	else:
+		weechat.prnt('moc: Unknown command %s' % (args[0]), '', server)
+		return weechat.PLUGIN_RC_OK
+
+def moc_infobar_update():
+	_load_settings()
+	if infobar['enabled'] == False:
+		return weechat.PLUGIN_RC_OK
+
+	if _get_status() == STATUS_STOPPED:
+		weechat.print_infobar(infobar['update']+1, 'moc is not currently playing')
+		return weechat.PLUGIN_RC_OK
+	else:
+		song = _get_song_info()
+		format = _format_np(infobar['format'], song, 'infobar')
+		weechat.print_infobar(infobar['update']+1, format)
+		return weechat.PLUGIN_RC_OK
+
+def moc_now_playing(server, formatType):
+	_load_settings()
+	format = ''
+	if formatType == '':
+		formatType = output['type']
+
+	if _get_status() == STATUS_STOPPED:
+		format = output['nothing']
+	else:
+		song = _get_song_info()
+		format = _format_np(output['format'], song, 'chat')
+
+	if formatType == 'i':
+		weechat.prnt(format)
+	elif formatType == 'o':
+		weechat.command(format)
+	elif formatType == 'ot':
+		weechat.command('/me %s' % format)
+
+	return weechat.PLUGIN_RC_OK
+
+def moc_unload():
+	"""Unload the plugin from weechat"""
+	if infobar['enabled']:
+		weechat.remove_infobar(0)
+		weechat.remove_timer_handler('moc_infobar_update')
+	return weechat.PLUGIN_RC_OK
+
+def _execute_command(cmd):
+	from subprocess import PIPE
+	proc = subprocess.Popen(cmd, shell = True, stderr = PIPE, stdout = PIPE, close_fds = True)
+	error = proc.stderr.read()
+	if error != '':
+		weechat.prnt(error)
+	output = proc.stdout.read()
+	proc.wait()
+	return output
+
+def _format_np(np, song, npType):
+	np = np.replace('%mocTitle%', song['Title'])
+	np = np.replace('%title%', song['SongTitle'])
+
+	if npType == 'chat':
+		if song['Artist'] != 'unknown':
+			np = np.replace('%artist%', output['artist'])
+			np = np.replace('%artist%', song['Artist'])
+		else:
+			np = np.replace('%artist%', '')
+
+		if song['Album'] != 'unknown':
+			np = np.replace('%album%', output['album'])
+			np = np.replace('%album%', song['Album'])
+		else:
+			np = np.replace('%album%', '')
+	else:
+		np = np.replace('%artist%', song['Artist'])
+		np = np.replace('%album%', song['Album'])
+
+	np = np.replace('%cTime%', song['CurrentTime'])
+	np = np.replace('%cSec%', song['CurrentSec'])
+	np = np.replace('%tTime%', song['TotalTime'])
+	np = np.replace('%tSec%', song['TotalSec'])
+	np = np.replace('%bitrate%', song['Bitrate'])
+	np = np.replace('%avgBitrate%', song['AvgBitrate'])
+	np = np.replace('%rate%', song['Rate'])
+	np = np.replace('%file%', song['File'])
+	np = np.replace('%C', chr(3))
+
+	if _get_status() == STATUS_PAUSED:
+		np = np + " - [PAUSED]"
+
+	return np
+
+def _get_song_info():
+	"""Get the song information from moc"""
+	song = {}
+	song['TotalTime'] = '?:??'
+	song['TotalSec'] = '??'
+
+	info = _execute_command('mocp -i')
+	for line in info.split('\n'):
+		if line != '':
+			index = line.find(': ')
+			name = line[:index]
+			value = line[index+2:]
+			if value == '':
+				value = 'unknown'
+			song[name] = value.strip()
+
+	if song['File'].find("://") < 0:
+		song['File'] = os.path.basename(song['File'])
+
+	return song
+
+def _get_status():
+	return _execute_command('mocp -i | grep "State:" | cut -d " " -f 2').strip()
+
+def _load_settings():
+    infobar['enabled'] = _load_setting('infobar_enabled', '0', 'bool')
+    infobar['format'] = _load_setting('infobar_format', 'Now Playing: %mocTitle%')
+    infobar['update'] = _load_setting('infobar_update', '10', 'int')
+    output['format'] = _load_setting('output_format', 'is listening to %C04%title%%C %artist%%album%::: %C07%file%%C ::: %cTime%/%tTime% @ %bitrate%')
+    output['artist'] = _load_setting('output_format_artist', '- %C03%artist%%C ')
+    output['album'] = _load_setting('output_format_album', '(%C12%album%%C) ')
+    output['type'] = _load_setting('output_type', 'ot')
+    output['nothing'] = _load_setting('output_nothing', 'is listening to nothing')
+
+def _load_setting(setting, default=None, type=None):
+	value = weechat.get_plugin_config(setting)
+	if value == '' and default != None:
+		weechat.set_plugin_config(setting, default)
+		value = default
+
+	if type == 'int' or type == 'bool':
+		value = int(value)
+
+	if type == 'bool':
+		value = bool(value)
+
+	return value
+
+if weechat.register('moc-control', '1.1.2', 'moc_unload', 'moc control and now playing script for Weechat'):
+	_load_settings()
+	if infobar['enabled']:
+		moc_infobar_update()
+		weechat.add_timer_handler(infobar['update'], 'moc_infobar_update')
+	weechat.add_command_handler(
+		'moc',
+		'moc_command',
+		'Control moc or display now playing information.',
+		'i|o|ot|play|pause|pp|stop|prev|next|infobar|help',
+		'Commands Available\n'
+		'  /moc         - Display currently playing song.\n'
+		'  /moc i       - Show info about current song\n'
+		'  /moc o       - Display currently playing song as /msg\n'
+		'  /moc ot      - Display currently playing song as /me\n'
+		'  /moc play    - Start playing music.\n'
+		'  /moc pause   - Toggle between pause/playing.\n'
+		'  /moc pp      - Toggle between pause/playing.\n'
+		'  /moc stop    - Stop playing music.\n'
+		'  /moc prev    - Move to the previous song in the playlist.\n'
+		'  /moc next    - Move to the next song in the playlist.\n'
+		'  /moc infobar - Toggle the infobar display.\n'
+		'\n'
+		'Formatting\n'
+		'  %mocTitle%   - Replaced with the title from moc.\n'
+		'  %artist%     - Replaced with the song artist.\n'
+		'  %title%      - Replaced with the song title.\n'
+		'  %album%      - Replaced with the song album.\n'
+		'  %file%       - Replaced with the filename/url of the song.\n'
+		'  %cTime%      - Replaced with how long the song has been playing.\n'
+		'  %cSec%       - Replaced with how long the song has been playing (seconcs).\n'
+		'  %tTime%      - Replaced with the length of the song.\n'
+		'  %tSec%       - Replaced with the length of the song (seconcs).\n'
+		'  %bitrate%    - Replaced with the bitrate of the song.\n'
+		'  %avgBitrate% - Replaced with the AvgBitrate of the song.\n'
+		'  %rate%       - Replaced with the rate of the song.\n'
+		'  %C##         - Make ## the number code of the color you want to use. Use %C by itself to end the color.\n'
+		'\n'
+		'To see all available settings, please check /setp moc\n',
+		'i|o|ot|play|pause|pp|stop|prev|next|infobar|help'
+	)

From be5ed9b4e18b0706bc989c0efdf672d9142f95a1 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Wed, 27 Jan 2010 23:06:34 +0100
Subject: [PATCH 20/21] Add new script foo_spam.pl

---
 perl/foo_spam.pl | 848 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 848 insertions(+)
 create mode 100644 perl/foo_spam.pl

diff --git a/perl/foo_spam.pl b/perl/foo_spam.pl
new file mode 100644
index 00000000..26352d9d
--- /dev/null
+++ b/perl/foo_spam.pl
@@ -0,0 +1,848 @@
+#! /usr/bin/env perl
+#
+# foo_spam - Prints the currently playing song from foobar2000.
+# 
+# Copyright (c) 2009-2010, Diogo Franco <diogomfranco@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use warnings;
+use strict;
+use utf8;
+use Encode;
+
+use Net::Telnet;
+use File::Path;
+use Time::HiRes qw(usleep);
+
+BEGIN {
+	*HAVE_XCHAT = Xchat->can('register') ? sub {1} : sub {0};
+	*HAVE_IRSSI = Irssi->can('command_bind') ? sub{1} : sub{0};
+	*HAVE_WEECH = weechat->can('register') ? sub {1} : sub {0};
+}
+
+my $ver = '0.6.1';
+my %info = (
+	author      => 'Kovensky',
+	contact     => '#shameimaru@irc.rizon.net',
+	url         => 'http://repo.or.cz/w/foo_spam.git',
+	name        => 'foo_spam',
+	description => 'Prints the currently playing song from foobar2000.',
+	license     => 'ISC'
+);
+
+if (HAVE_IRSSI) {
+	our $VERSION = $ver;
+	our %IRSSI = %info;
+}
+
+Xchat::register($info{name}, $ver, $info{description}, \&close_telnet) if HAVE_XCHAT;
+weechat::register($info{name}, $info{author}, $ver, $info{license}, $info{description}, 'close_telnet', 'UTF-8') if HAVE_WEECH;
+# ChangeLog:
+# 0.6.1 - Added weechat support.
+# 0.6   - Backwards incompatible version. Changes the format syntax, documents functions, implement some others.
+# 0.5.2 - Added discnumber and totaldiscs tags. Changed default format. Silences a warning when a function ends on ",)". Fixed two warnings in the $if family.
+# 0.5.1 - Fixed $if, $if2, $and, $or and $xor behavior on certain strings.
+# 0.5   - Support subfunctions and tags with underlines. Changed some other details.
+# 0.4   - Fixed UTF-8 corruption issues. Allow the user to specify a comment when using /aud by giving it as an argument. Document build_output.
+# 0.3.2 - Change the method used to read foobar2000's response. The previous method would hang once in a while.
+# 0.3.1 - Change default settings to avoid breakage if a track has | on one of the tags. Update documentation.
+# 0.3   - Allow customization of the format string. Changed method of desync handling.
+# 0.2.2 - Fix desync issues if foobar takes "too long" to respond. Added codec and bitrate to the output.
+# 0.2.1 - Forgot to handle one error case on the telnet connection.
+# 0.2   - Changed the recommended string and output. Fixed my wrong XChat API usage. Changed the way the telnet connection is handled.
+# 0.1   - First version
+
+# Known Bugs:
+# Doesn't support tags that are equal to "?" (foo_controlserver limitation).
+
+# TODO:
+# Replace the current format syntax by foobar2000's title format
+
+our $telnet_open    = 0;
+our $telnet         = undef;
+our $default_format = <<'EOF';
+$left(%_foobar2000_version%,10) ($replace(%_foobar2000_version%,foobar2000 ,)):
+ [%album artist% ]'['[%date% ][%album%][ #[%discnumber%.]%tracknumber%[/[%totaldiscs%.]%totaltracks%]]']'
+ [%track artist% - ]%title% '['%playback_time%[/%length%]']'[ %bitrate%kbps][ %codec%[ %codec_profile%]][ <-- %comment%]
+EOF
+$default_format =~ s/\R//g;
+our $format         = $default_format;
+our %heap;
+
+our $setting_file   = undef; # Only used by Xchat
+
+sub open_telnet {
+	$telnet = new Net::Telnet(Port => 3333, Timeout => 10, Errmode => 'return') if not defined($telnet);
+	$telnet_open = $telnet->open("localhost");
+	unless($telnet_open) {
+		irc_print("Error connecting to foobar2000! Make sure fb2k is running.");
+		irc_print("Also check if foo_controlserver is properly configured.");
+	}
+	return $telnet_open;
+}
+
+sub close_telnet {
+	if($telnet_open) {
+		$telnet_open = 0;
+		$telnet->put("exit\n");
+		$telnet->close;
+	}
+}
+
+sub get_track_info {
+	return undef unless open_telnet();
+
+	my $line = undef;
+
+	unless (defined($telnet->print("trackinfo"))) {
+		close_telnet();
+		return undef unless open_telnet();
+	}
+
+	my @result = $telnet->waitfor(Match => '/11[123]\|+.+?\|+.+?\|+(?!0\.[0-5][0-9]).*/', Timeout => 5);
+
+	$line = $result[1] if @result;
+	
+	close_telnet();
+	
+	unless($line) {
+		irc_print("Error retrieving status from foobar2000!");
+		return undef;
+	}
+	
+	unless (eval { $line = decode("UTF-8", $line, Encode::FB_CROAK) }) {
+		irc_print("Error: line is not valid UTF-8. Check foo_controlserver's settings.");
+		return undef;
+	}
+	
+	%heap = ();
+
+	my @fields;
+
+	if($line =~ /^11.\|\|\|/ and $line =~ /\|\|\|(.*?)\|\|\|$/) { # proper setting
+		@fields = split(/\|\|\|/, $line);
+	} else {                    # the luser didn't configure it correctly
+		$line =~ s/\|\|\|/\|/g; # fix possible half-configuration
+		@fields = split(/\|/, $line);
+	}
+
+	# Standard settings
+	my $info = {state                  => $fields[0],
+	            playback_time_seconds  => $fields[3],
+	            codec                  => $fields[4],
+	            bitrate                => $fields[5],
+	            'album artist'         => $fields[6],
+	            album                  => $fields[7],
+	            date                   => $fields[8],
+	            genre                  => $fields[9],
+	            tracknumber            => $fields[10],
+	            title                  => $fields[11]};
+	if ($fields[19]) { # 
+		$info->{'artist'}              = $fields[12];
+		$info->{'totaltracks'}         = $fields[13];
+		$info->{'playback_time'}       = $fields[14];
+		$info->{'length'}              = $fields[15];
+		
+		$info->{'_foobar2000_version'} = $fields[16];
+		
+		$info->{'codec_profile'}       = $fields[17];
+
+		$info->{'discnumber'}          = $fields[18];
+		$info->{'totaldiscs'}          = $fields[19];
+	}
+
+	$info->{'isplaying'} = 1;
+	$info->{'ispaused'} = 0;
+	if ($info->{'state'} eq "113") {
+		$info->{'ispaused'} = 1;
+	} elsif ($info->{'state'} eq "112") {
+		$info->{'isplaying'} = 0;
+	}
+	delete $info->{'state'};
+
+	for (keys %$info) {
+		delete $info->{$_} if (defined($info->{$_}) and $info->{$_} eq '?');
+	}
+
+	$info->{'album artist'} = $info->{'artist'} unless defined($info->{'album artist'});
+	$info->{'track artist'} = $info->{'artist'} if (defined($info->{'artist'}) and $info->{'album artist'} ne $info->{'artist'});
+	
+	if (defined($info->{'length'})) {
+		my ($h, $m, $s) = split(/\:/, $info->{'length'});
+		if (defined $s) {
+			$info->{'length_seconds'} = $s + $m * 60 + $h * 3600;
+		} else {
+			$info->{'length_seconds'} = $m + $h * 60;
+		}
+	}
+	
+	if ($info->{'length_seconds'} and $info->{'playback_time_seconds'}) {
+		$info->{'playback_time_remaining_seconds'} = 
+				$info->{'length_seconds'} - $info->{'playback_time_seconds'};
+	}
+
+	for (('playback_time', 'playback_time_remaining')) {
+		unless (defined($info->{$_})) {
+			my $t = $info->{"${_}_seconds"};
+
+			my @u = (0,0);
+			for (my $i = 1; $i >= 0; $i--) {
+				$u[$i] = $t % 60;
+				$t = int($t / 60);
+			}
+			$info->{$_} = sprintf("%s%02d:%02d", $t > 0 ? "$t:" : "", @u[0,1]);
+		}
+	}
+
+	return $info;
+}
+
+sub parse_format {
+	my ($format, $info, $sublevel) = @_;
+	$sublevel = 0 if not defined $sublevel;
+
+	my $output = "";
+
+	$format =~ s/\R//g; # ignore line breaks
+	my @chars = split(//,$format);
+
+	# Language Definition
+
+	# lowercasestring      <== should be parsed as a tag name, makes the expression fail if such tag is not defined
+	# []                   <== brackets allow the parsing inside them to fail
+	# $func(arg1,arg2,...) <== function call (see parse_subfunction for details)
+	# ''                   <== string literal (ignores all parsing)
+	# \(character)         <== literal character
+
+	# Bracket Nesting
+
+	# A bracket returns a defined value only if it has at least one tag or at least one of its embedded brackets return true.
+
+	my @tokens = ();
+	my $tagcount = 0;
+	my $fail = 0;
+
+	my $literal = 0;
+	my $sub = 0;
+	my $func = 0;
+	my $tagmode = 0;
+	my $str = "";
+	my $ignore = 0;
+
+	for(my $i = 0; $i < @chars; $i++ ) { # 1st Pass (Lexical analysis, push into @tokens)
+		if($literal) { # If on literal mode
+			$str .= $chars[$i]; # Simply copy everything as-is until an unescaped ' is found
+			if ($chars[$i] eq "'") {
+				push @tokens, $str;
+				$str = "";
+				$literal = 0;
+			} elsif (not defined($chars[$i+1])) { # This means we ended the string with an uneven number of unescaped 's
+				warn "Malformed: mismatched ': $str";
+				return undef;
+			}
+		} elsif ($sub) { # If on subexpression mode
+			$str .= $chars[$i]; # Copy everything as-is until an unescaped ] is found
+			if ($chars[$i] eq "'") {
+				$ignore = !$ignore;
+			} elsif ($chars[$i] eq "[") { # We must copy any sub-subexpressions inside this sub-expression for recursive evaluation
+				++$sub unless $ignore;
+			} elsif ($chars[$i] eq "]" and !$ignore and --$sub == 0) {
+				push @tokens, $str;
+				$str = "";
+			} elsif (not defined($chars[$i+1])) { # This means we ended the string without $sub being 0
+				warn "Malformed: mismatched [: $str";
+				return undef;
+			}
+		} elsif ($tagmode) { # If on tag mode
+			$str .= $chars[$i]; # Copy tags as-is until any % character is found
+			if ($chars[$i] eq '%') {
+				push @tokens, $str;
+				$str = "";
+				$tagmode = 0;
+			} elsif (not defined($chars[$i+1])) {
+				warn "Malformed: mismatched %: $str";
+				return undef;
+			}
+		} elsif ($func) { # If on function mode
+			$str .= $chars[$i]; # Copy everything until an unescaped ) is found
+			if ($chars[$i] eq "'") {
+				$ignore = !$ignore;
+			} elsif ($chars[$i] eq "(") {
+				$func++ unless $ignore;
+			} elsif ($chars[$i] eq ")" and !$ignore and --$func <= 1) {
+				push @tokens, $str;
+				$str = "";
+				$func = 0;
+			} elsif (not defined($chars[$i+1])) {
+				warn "Malformed: mismatched (: $str";
+				return undef;
+			}
+		} else {
+			if ($chars[$i] eq "'") {
+				push @tokens, "$str" if $str ne ""; # Found an opening quote
+				$str = $chars[$i];
+				$literal = 1; # Enter literal mode
+			} elsif ($chars[$i] eq "[") {
+				push @tokens, "$str" if $str ne ""; # Found a subexpression opener
+				$str = $chars[$i];
+				$sub = 1; # Enter subexpression mode
+			} elsif ($chars[$i] eq "\$") {
+				push @tokens, "$str" if $str ne "";
+				$str = $chars[$i];
+				$func = 1; # Enter subfunction mode
+			} elsif ($chars[$i] eq "%") {
+				push @tokens, "$str" if $str ne ""; # Found a tag name
+				$str = $chars[$i];
+				$tagmode = 1; # Enter tag mode
+			} else {
+				$str .= $chars[$i]; # Copy as a literal
+			}
+		}
+	}
+
+	push @tokens, "$str" if $str ne ""; # Make sure whatever is left from parsing is added as a literal
+
+	foreach my $token (@tokens) { # 2nd Pass, execute tokens
+		if ($token =~ /^'(.*)'$/ or $token =~ /^([^['%\$].*)$/) { # If the token is a literal, then
+			$output .= $token eq "''" ? "'" : $1; # '' means a literal ', otherwise literal contents
+		} elsif ($token =~ /^%(.*)%$/) { # If this is a tag
+			$token = $1;
+			return undef unless defined($info->{$token});
+			$output .= $info->{$token}; # Copy value to output
+		} elsif ($token =~ /^\[(.*)\]$/) { # If the token is a subexpression
+			$token = $1;
+			my $recurse = parse_format($token, $info, $sublevel+1); # Recurse
+			$output .= $recurse if defined($recurse);
+		} elsif ($token =~ /^\$/) { # If the token is a subfunction
+			my $res = parse_subfunction($token, $info, $sublevel);
+			return undef unless defined($res);
+			$output .= $res;
+		} else {
+			warn "Parsing error: $token";
+			return undef;
+		}
+	}
+
+	return $output;
+}
+
+sub build_output {
+	my ($format, $info, $sublevel) = @_;
+	$sublevel = 0 if not defined $sublevel;
+
+	return parse_format($format, $info, $sublevel);
+}
+
+sub parse_subfunction {
+	my ($function, $info, $sublevel) = @_;
+
+	$function =~ /^\$(.*?)\((.*)\)$/;
+
+	my $func = $1;
+
+	my @rawargs = split(//, $2);
+	my @args = ();
+
+	my $ignore = 0;
+	my $str = "";
+	for(my $i = 0; $i < @rawargs; $i++) {
+		if ($rawargs[$i] eq "'") {
+			$ignore = !$ignore;
+		} elsif ($rawargs[$i] eq ",") {
+			unless ($ignore) {
+				push @args, $str;
+				$str = "";
+				++$i;
+			}
+		}
+		$str .= $rawargs[$i] if defined($rawargs[$i]);
+	}
+	push @args, $str;
+
+	for (my $i = 0; $i < @args; $i++) {
+		$args[$i] = parse_format($args[$i], $info, $sublevel+1);
+	}
+
+	if ($func eq "len") {
+		return defined $args[0] ? length($args[0]) : undef;
+	} elsif ($func eq "repeat") {
+		return (defined $args[0] and defined $args[1]) ? ($args[0] x $args[1]) : undef;
+	} elsif ($func eq "trim") {
+		my ($str) = @args;
+		return undef unless defined $str;
+		$str =~ /^\s*+(.*?)\s*+$/;
+		return $1;
+	} elsif ($func eq "put" or $func eq "puts") {
+		my ($var, $val) = @args;
+		return undef unless (defined $var and defined $val);
+		$heap{$var} = $val;
+		return ($func eq "put") ? $val : "";
+	} elsif ($func eq "get") {
+		my ($var) = @args;
+		return undef unless defined $var;
+		return exists $heap{$var} ? $heap{$var} : "";
+	} elsif ($func eq "pad" or $func eq "pad_right" or $func eq "left" or $func eq "cut" or $func eq "padcut" or $func eq "padcut_right") {
+		my ($str, $maxlen, $char) = @args;
+		return undef unless (defined $str and $maxlen);
+		
+		my $pad = ($func eq "pad" or $func eq "pad_right" or $func eq "padcut" or $func eq "padcut_right");
+		my $cut = ($func eq "left" or $func eq "cut" or $func eq "padcut" or $func eq "padcut_right");
+		
+		if ($cut) {
+			$str = substr($str, 0, $maxlen);
+		}
+		if ($pad) {
+			$char = " " unless defined $char and $char ne "";
+			$char = substr($char, 0, 1);
+			$str .= ($char x ($maxlen - length($str)));
+		}
+		return $str;
+	} elsif ($func eq "right") {
+		my ($str, $maxlen) = @args;
+		return undef unless (defined $str and defined $maxlen);
+		return substr($str, -$maxlen);
+	} elsif ($func eq "insert" or $func eq "replace") {
+		my ($haystack, $needle, $pos) = @args;
+		return undef unless (defined($haystack) and defined($needle) and defined($pos));
+		if ($func eq "insert") {
+			return substr($haystack, 0, $pos) . $needle . substr($haystack, $pos);
+		}
+		$needle = quotemeta($needle);
+		$haystack =~ s/$needle/$pos/g;
+		return $haystack;
+	} elsif ($func eq "if" or $func eq "if2") {
+		my ($test, $iftrue, $iffalse);
+		if ($func eq "if") {
+			($test, $iftrue, $iffalse) = @args;
+		} else {
+			($test, $iffalse) = @args;
+			$iftrue = $test;
+		}
+
+		if ($test) {
+			return $iftrue;
+		} else {
+			return $iffalse;
+		}
+	} elsif ($func eq "if3") {
+		foreach (@args) {
+			return $_ if $_;
+		}
+		return undef;
+	} elsif ($func eq "greater") {
+		my ($arg1, $arg2) = @args;
+		return undef unless (defined($arg1) or defined($arg2));
+		return $arg1 unless defined $arg2;
+		return $arg2 unless defined $arg1;
+		return $arg1 if $arg1 >= $arg2;
+		return $arg2;
+	} elsif ($func eq "longer") {
+		my ($arg1, $arg2) = @args;
+		return undef unless (defined($arg1) or defined($arg2));
+		return $arg1 unless defined $arg2;
+		return $arg2 unless defined $arg1;
+		return $arg1 if length($arg1) >= length($arg2);
+		return $arg2;
+	} elsif ($func eq "longest") {
+		return undef unless scalar(@args);
+		my $longest = $_[0];
+		foreach (@args) {
+			next unless defined;
+			$longest = $_ if length($_) > length($longest);
+		}
+		return $longest;
+	} elsif ($func eq "ifgreater" or $func eq "ifequal" or $func eq "iflonger") {
+		my ($arg1, $arg2, $iftrue, $iffalse) = @args;
+
+		unless (defined($arg2)) {
+			return $iftrue if (defined($arg1));
+			return $iffalse;
+		}
+		return $iffalse unless (defined($arg1));
+
+		if ($func eq "iflonger") {
+			return defined($arg1) ? $iftrue : $iffalse unless (defined($arg1) and defined($arg2));
+			return $iftrue if (length($arg1) > length(" " x $arg2));
+		} elsif ($func eq "ifequal") {
+			# Any of the args may not be comparable, return false in that case
+			return $iftrue if (defined($arg1) and defined($arg2));
+			return $iffalse unless (defined($arg1) and defined($arg2));
+			eval { return $iftrue if $arg1 == $arg2 };
+		} else { # ifgreater
+			return defined($arg1) ? $iftrue : $iffalse unless (defined($arg1) and defined($arg2));
+			eval { return $iftrue if $arg1 > $arg2 };
+		}
+		return $iffalse;
+	} elsif ($func eq "abbr") {
+		my ($arg1, $arg2) = (0,0);
+		$arg1 = $args[0];
+		$arg2 = $args[1] if (defined($args[1]));
+		return undef unless (defined $arg1 and $arg2 >= 0);
+
+		if (length($arg1) > $arg2) {
+			my $abbr = "";
+			my @tokens = split(/\s+/, $arg1);
+			foreach my $token (@tokens) {
+				my @chars = split(//, $token);
+				$abbr .= $chars[0];
+			}
+			return $abbr;
+		}
+		return $arg1;
+	} elsif ($func eq "num") {
+		my ($arg1, $arg2) = @args;
+		return undef unless (defined($arg1) and $arg2 > 0);
+		return sprintf("%0${arg2}d", $arg1);
+	} elsif ($func =~ /^(add|sub|mul|div|mod|max|min)$/) {
+		my ($arg1, $arg2) = @args;
+		return undef unless (defined($arg1) and defined($arg2));
+		# Make sure both are numbers. Better way to do this?
+		return undef unless eval { $arg1 != $arg2 or $arg1 == $arg2 };
+		return $arg1 + $arg2 if ($func eq "add");
+		return $arg1 - $arg2 if ($func eq "sub");
+		return $arg1 * $arg2 if ($func eq "mul");
+		return $arg1 / $arg2 if ($func eq "div");
+		return $arg1 % $arg2 if ($func eq "mod");
+		return ($arg1 >= $arg2 ? $arg1 : $arg2) if ($func eq "max");
+		return ($arg1 < $arg2 ? $arg1 : $arg2) if ($func eq "min");
+	} elsif ($func =~ /^(and|or|xor|not)$/) {
+		my ($arg1, $arg2) = @args;
+		$arg1 = 0 unless defined $arg1;
+		$arg2 = 0 unless defined $arg2;
+
+		# Need to give explicit returns to avoid eating on parse_format
+
+		return ($arg1 ? 0 : 1) if ($func eq "not");
+		return (($arg1 && $arg2) ? 1 : 0) if ($func eq "and");
+		return (($arg1 || $arg2) ? 1 : 0) if ($func eq "or");
+		return (($arg1 && !$arg2) ? 1 : ((!$arg1 && $arg2) ? 1 : 0)) if ($func eq "xor");
+	} elsif ($func eq "strcmp" or $func eq "stricmp") {
+		my ($arg1, $arg2) = @args;
+		return undef unless (defined($arg1) and defined($arg2));
+		return ((lc($arg1) eq lc($arg2)) ? 1 : 0) if ($func eq "stricmp");
+		return (($arg1 eq $arg2) ? 1 : 0);
+	} elsif ($func eq "caps") {
+		my ($arg1) = @args;
+		return undef unless defined $arg1;
+		$arg1 =~ s/\b(\S)(\S*)\b/@{[uc($1)]}@{[lc($2)]}/g;
+		return $arg1;
+	} elsif ($func eq "caps2") {
+		my ($arg1) = @args;
+		return undef unless defined $arg1;
+		$arg1 =~ s/\b(\S)/@{[uc($1)]}/g;
+		return $arg1;
+	} elsif ($func eq "lower" or $func eq "upper") {
+		my ($arg1) = @args;
+		return undef unless defined $arg1;
+		return lc($arg1) if $func eq "lower";
+		return uc($arg1);
+	} elsif ($func eq "fix_eol") {
+		my ($meta, $repl) = @args;
+		$repl = " (...)" unless $repl;
+		return undef unless defined($meta);
+		$meta =~ s/\010?\013.*//;
+		return $meta;
+	}
+
+	warn "Unknown or unimplemented function: $function";
+	return undef;
+}
+
+sub get_np_string {
+	my $info = get_track_info();
+	$info->{comment} = $_[0] if $_[0];
+	if (defined($info)) {
+		return build_output($format, $info);
+	}
+	return undef;
+}
+
+sub get_help_string {
+	my $fields;
+	if (HAVE_IRSSI) {
+		$fields = '%%codec%%|||%%bitrate%%|||%%album artist%%|||%%album%%|||%%date%%|||%%genre%%|||%%tracknumber%%|||%%title%%|||%%artist%%|||%%totaltracks%%|||%%playback_time%%|||%%length%%|||%%_foobar2000_version%%|||%%codec_profile%%|||%%discnumber%%|||%%totaldiscs%%';
+	} else {
+		$fields = '%codec%|||%bitrate%|||%album artist%|||%album%|||%date%|||%genre%|||%tracknumber%|||%title%|||%artist%|||%totaltracks%|||%playback_time%|||%length%|||%_foobar2000_version%|||%codec_profile%|||%discnumber%|||%totaldiscs%';
+	}
+	my $help = <<EOF
+Required Plugin: foo_controlserver
+URL: http://www.hydrogenaudio.org/forums/index.php?showtopic=38114
+Required settings: Control Server tab:
+* Server Port: 3333
+* UTF-8 output/input: checked
+* Base delimiter: |||
+Recommended setting:
+* Number of Clients: Some big number like 700
+* Fields: $fields
+
+NOTE: the script only works with either the default or this custom Fields line.
+
+This script can also work via SSH tunneling, by using -R 3333:localhost:3333.
+
+EOF
+;
+	return $help;
+}
+
+sub get_intro_string {
+	my $intro = <<EOF
+\002-----------------------------------------------------------------
+\002foo_spam - prints the currently playing track from foobar2000
+\002Created by Kovensky \(irc.rizon.net #shameimaru\)
+This script requires a properly configured foobar2000.
+Run /foo_help for help setting foobar2000 up.
+\002-----------------------------------------------------------------
+Usage:
+/aud        - prints the playing song as an ACTION
+/np         - alias to /aud
+/foo_help   - explains how to set up foobar2000
+/foo_format - explains how to set up the output format
+\002-----------------------------------------------------------------
+
+EOF
+;
+	return $intro;
+}
+
+sub get_foo_format_help_string {
+	my $help = <<EOF
+Format Definition
+Example: %artist% - [%album% - ]%title%
+
+foo_spam now uses the same syntax as foobar2000 (title format), however only
+a subset of it is currently implemented. To see the list of supported
+tags, use /foo_tags. To see the list of supported functions, use
+/foo_funcs.
+
+To change the format, you can use:
+ * Irssi: /set foo_format <new format> (use /set -default to reset)
+ * X-Chat: /set_foo_format <new format> (use /set_foo_format default to reset)
+ * WeeChat: /set plugins.var.foo_spam.format <new format> (use /unset to reset)
+You can also edit the script and change the value of \$default_format, in case
+you use an unsupported client.
+
+Default: $default_format
+
+EOF
+;
+	return $help;
+}
+
+sub get_taglist_string {
+	my $list = <<EOF
+List of available tags (refer to foobar2000's documentation for their meanings):
+ - %isplaying%, %ispaused%, %_foobar2000_version%
+ - %playback_time%, %playback_time_remaining%, %length% (plus the _seconds variants)
+ - %artist%, %album artist%, %track artist%, %album%, %title%, %genre%
+ - %date%, %discnumber%, %totaldiscs%, %tracknumber%, %totaltracks%
+ - %codec%, %bitrate%, %codec_profile%
+The %comment% tag is set by foo_spam itself and it contains all arguments that the user gives to /aud in a single string.
+EOF
+;
+	return $list;
+}
+
+sub get_funclist_string {
+	my $list = <<'EOF'
+List of available functions (refer to foobar2000's documentation for their meanings):
+ - $if(X,Y,Z), $if2(X,Y), $if3(X,Y,Z,...), $ifgreater(A,B,C,D), $iflonger(A,B,C,D), $ifequal(A,B,C,D)
+ - $and(X,Y), $or(X,Y), $xor(X,Y), $not(X)
+ - $strcmp(X,Y), $stricmp(X,Y), $len(X), $num(X,Y)
+ - $greater(X,Y), $longer(X,Y), $longest(A,B,C,...)
+ - $caps(X), $caps2(X), $lower(X), $upper(X)
+ - $trim(A), $pad(X,Y), $pad_right(X,Y), $pad(X,Y,Z), $pad_right(X,Y,Z), $left(X,Y), $cut(X,Y), $padcut(X,Y), $padcut_right(X,Y), $right(X,Y)
+ - $insert(A,B,N), $replace(A,B,C), $repeat(X,N)
+ - $abbr(X), $abbr(X,Y)
+ - $add(X,Y), $sub(X,Y), $mul(X,Y), $div(X,Y), $mod(X,Y), $min(X,Y), $max(X,Y)
+ - $put(name,text), $puts(name,text), $get(name)
+EOF
+;
+	return $list;
+}
+
+if (HAVE_IRSSI) {
+	*print_now_playing = sub {
+		my ($data, $server, $witem) = @_;
+		my $str = get_np_string(decode("UTF-8", $data));
+		if (defined($str)) {
+			if ($witem && ($witem->{type} eq "CHANNEL" ||
+				$witem->{type} eq "QUERY")) {
+				$witem->command(encode_utf8("me $str"));
+			}
+		}
+	};
+
+	*print_foo_help = sub{
+		Irssi::print(get_help_string());
+	};
+
+	*print_foo_format_help = sub {
+		my $help = get_foo_format_help_string();
+		$help =~ s/%/%%/g;
+		Irssi::print($help);
+	};
+
+	*irc_print = sub {
+		Irssi::print($_[0]);
+	};
+
+	*print_foo_tags = sub {
+		my $help = get_foo_taglist_string();
+		$help =~ s/%/%%/g;
+		Irssi::print($help);
+	};
+	
+	*print_foo_funcs = sub {
+		Irssi::print(get_funclist_string());
+	};
+
+	Irssi::settings_add_str("foo_spam", "foo_format", $format);
+	$format = Irssi::settings_get_str("foo_format");
+
+	Irssi::command_bind('aud', 'print_now_playing');
+	Irssi::command_bind('np', 'print_now_playing');
+	Irssi::command_bind('foo_help', 'print_foo_help');
+	Irssi::command_bind('foo_format','print_foo_format_help');
+	Irssi::command_bind('foo_tags','print_foo_tags');
+	Irssi::command_bind('foo_funcs','print_foo_funcs');
+} elsif (HAVE_XCHAT) {
+	*print_now_playing = sub {
+		my $str = get_np_string($_[0][1] ? $_[1][1] : undef);
+		if (defined($str)) {
+			Xchat::command(encode_utf8("me $str"));
+		}
+		return Xchat::EAT_ALL();
+	};
+
+	*print_foo_help = sub {
+		Xchat::print(get_help_string());
+		return Xchat::EAT_ALL();
+	};
+
+	*irc_print = sub {
+		Xchat::print(@_);
+	};
+
+	*set_foo_format = sub {
+		if (defined($_[0][1])) {
+			open($setting_file, ">", Xchat::get_info('xchatdir') . "/foo_spam.conf");
+			if ($_[0][1] eq "default") {
+				$format = $default_format;
+			} else {
+				$format = $_[1][1];
+			}
+			Xchat::print("Changed format to $format\n");
+			if (defined($setting_file)) {
+				print $setting_file $format;
+				close($setting_file);
+			} else {
+				Xchat::print("Failed to save settings! Error: $!");
+			}
+		} else {
+			Xchat::print("Current format: $format\n");
+		}
+		return Xchat::EAT_ALL();
+	};
+	if (defined(*set_foo_format)) {} # Silence a warning
+
+	*print_foo_format_help = sub {
+		Xchat::print(get_foo_format_help_string());
+		return Xchat::EAT_ALL();
+	};
+
+	*print_foo_tags = sub {
+		Xchat::print(get_taglist_string());
+		return Xchat::EAT_ALL();
+	};
+	
+	*print_foo_funcs = sub {
+		Xchat::print(get_funclist_string());
+		return Xchat::EAT_ALL();
+	};
+
+	if (open($setting_file, "<", Xchat::get_info('xchatdir') . "/foo_spam.conf")) {
+		my $line = <$setting_file>;
+		chomp $line;
+		$format = $line if (defined($line) and $line ne "");
+		close($setting_file);
+	}
+
+	Xchat::hook_command("np","print_now_playing", {help => "alias to /aud"});
+	Xchat::hook_command("aud","print_now_playing", {help => "prints your currently playing song on foobar2000 on an ACTION"});
+	Xchat::hook_command("foo_help","print_foo_help", {help => "explains how to set up foobar2000"});
+	Xchat::hook_command("set_foo_format","set_foo_format", {help => "displays or changes the current format string"});
+	Xchat::hook_command("foo_format","print_foo_format_help", {help => "explains how to configure the format string"});
+	Xchat::hook_command("foo_tags","print_foo_tags", {help => "lists all available tags"});
+	Xchat::hook_command('foo_funcs','print_foo_funcs', {help => "lists all available functions"});
+} elsif (HAVE_WEECH) {
+	*print_now_playing = sub {
+		my ($data, $buffer, @args) = @_;
+		$format = weechat::config_get_plugin("format");
+		my $str = get_np_string($args[0] ? decode("UTF-8", join(' ', @args)) : undef);
+		if (defined($str)) {
+			weechat::command($buffer, encode_utf8("/me $str"));
+		}
+		return weechat::WEECHAT_RC_OK_EAT();
+	};
+
+	*irc_print = sub {
+		weechat::print('', shift);
+	};
+
+	*print_foo_help = sub {
+		irc_print(get_help_string());
+		return weechat::WEECHAT_RC_OK_EAT();
+	};
+
+	*print_foo_format_help = sub {
+		irc_print(get_foo_format_help_string());
+		return weechat::WEECHAT_RC_OK_EAT();
+	};
+
+	*print_foo_tags = sub {
+		irc_print(get_taglist_string());
+		return weechat::WEECHAT_RC_OK_EAT();
+	};
+	
+	*print_foo_funcs = sub {
+		irc_print(get_funclist_string());
+		return Xchat::WEECHAT_RC_OK_EAT();
+	};
+
+	unless (weechat::config_is_set_plugin("format")) {
+		weechat::config_set_plugin("format", $default_format);
+	}
+
+	weechat::hook_command('np', 'alias to /aud', '', '', '%(nicks)', 'print_now_playing', '');
+	weechat::hook_command('aud', 'prints your currently playing song on foobar2000 on an ACTION', '', '', '%(nicks)', 'print_now_playing', '');
+	weechat::hook_command('foo_help', 'explains how to set up foobar2000', '', '', '', 'print_foo_help', '');
+	weechat::hook_command('foo_format', 'explains how to configure the format string', '', '', '', 'print_foo_format_help', '');
+	weechat::hook_command('foo_tags', 'lists all available tags', '', '', '', 'print_foo_tags', '');
+	weechat::hook_command('foo_funcs', 'lists all available functions', '', '', '', 'print_foo_funcs', '');
+} else {
+	$| = 1;
+	binmode (STDERR, ":encoding(utf-8)");
+	binmode (STDOUT, ":encoding(utf-8)");
+	*irc_print = sub {
+		print (STDERR "@_\n") if @_;
+	};
+	$format = join(" ", @ARGV) if $ARGV[0];
+	my $np = get_np_string();
+	print "$np\n" if $np;
+}
+
+if (HAVE_XCHAT or HAVE_IRSSI or HAVE_WEECH) {
+	irc_print(get_intro_string());
+}
+

From 036fa2a61ed49188774d00cf43fc1f8541e24416 Mon Sep 17 00:00:00 2001
From: Sebastien Helleu <flashcode@flashtux.org>
Date: Wed, 27 Jan 2010 23:08:53 +0100
Subject: [PATCH 21/21] Remove script foo_spam.pl (wrong branch)

---
 perl/foo_spam.pl | 848 -----------------------------------------------
 1 file changed, 848 deletions(-)
 delete mode 100644 perl/foo_spam.pl

diff --git a/perl/foo_spam.pl b/perl/foo_spam.pl
deleted file mode 100644
index 26352d9d..00000000
--- a/perl/foo_spam.pl
+++ /dev/null
@@ -1,848 +0,0 @@
-#! /usr/bin/env perl
-#
-# foo_spam - Prints the currently playing song from foobar2000.
-# 
-# Copyright (c) 2009-2010, Diogo Franco <diogomfranco@gmail.com>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-use warnings;
-use strict;
-use utf8;
-use Encode;
-
-use Net::Telnet;
-use File::Path;
-use Time::HiRes qw(usleep);
-
-BEGIN {
-	*HAVE_XCHAT = Xchat->can('register') ? sub {1} : sub {0};
-	*HAVE_IRSSI = Irssi->can('command_bind') ? sub{1} : sub{0};
-	*HAVE_WEECH = weechat->can('register') ? sub {1} : sub {0};
-}
-
-my $ver = '0.6.1';
-my %info = (
-	author      => 'Kovensky',
-	contact     => '#shameimaru@irc.rizon.net',
-	url         => 'http://repo.or.cz/w/foo_spam.git',
-	name        => 'foo_spam',
-	description => 'Prints the currently playing song from foobar2000.',
-	license     => 'ISC'
-);
-
-if (HAVE_IRSSI) {
-	our $VERSION = $ver;
-	our %IRSSI = %info;
-}
-
-Xchat::register($info{name}, $ver, $info{description}, \&close_telnet) if HAVE_XCHAT;
-weechat::register($info{name}, $info{author}, $ver, $info{license}, $info{description}, 'close_telnet', 'UTF-8') if HAVE_WEECH;
-# ChangeLog:
-# 0.6.1 - Added weechat support.
-# 0.6   - Backwards incompatible version. Changes the format syntax, documents functions, implement some others.
-# 0.5.2 - Added discnumber and totaldiscs tags. Changed default format. Silences a warning when a function ends on ",)". Fixed two warnings in the $if family.
-# 0.5.1 - Fixed $if, $if2, $and, $or and $xor behavior on certain strings.
-# 0.5   - Support subfunctions and tags with underlines. Changed some other details.
-# 0.4   - Fixed UTF-8 corruption issues. Allow the user to specify a comment when using /aud by giving it as an argument. Document build_output.
-# 0.3.2 - Change the method used to read foobar2000's response. The previous method would hang once in a while.
-# 0.3.1 - Change default settings to avoid breakage if a track has | on one of the tags. Update documentation.
-# 0.3   - Allow customization of the format string. Changed method of desync handling.
-# 0.2.2 - Fix desync issues if foobar takes "too long" to respond. Added codec and bitrate to the output.
-# 0.2.1 - Forgot to handle one error case on the telnet connection.
-# 0.2   - Changed the recommended string and output. Fixed my wrong XChat API usage. Changed the way the telnet connection is handled.
-# 0.1   - First version
-
-# Known Bugs:
-# Doesn't support tags that are equal to "?" (foo_controlserver limitation).
-
-# TODO:
-# Replace the current format syntax by foobar2000's title format
-
-our $telnet_open    = 0;
-our $telnet         = undef;
-our $default_format = <<'EOF';
-$left(%_foobar2000_version%,10) ($replace(%_foobar2000_version%,foobar2000 ,)):
- [%album artist% ]'['[%date% ][%album%][ #[%discnumber%.]%tracknumber%[/[%totaldiscs%.]%totaltracks%]]']'
- [%track artist% - ]%title% '['%playback_time%[/%length%]']'[ %bitrate%kbps][ %codec%[ %codec_profile%]][ <-- %comment%]
-EOF
-$default_format =~ s/\R//g;
-our $format         = $default_format;
-our %heap;
-
-our $setting_file   = undef; # Only used by Xchat
-
-sub open_telnet {
-	$telnet = new Net::Telnet(Port => 3333, Timeout => 10, Errmode => 'return') if not defined($telnet);
-	$telnet_open = $telnet->open("localhost");
-	unless($telnet_open) {
-		irc_print("Error connecting to foobar2000! Make sure fb2k is running.");
-		irc_print("Also check if foo_controlserver is properly configured.");
-	}
-	return $telnet_open;
-}
-
-sub close_telnet {
-	if($telnet_open) {
-		$telnet_open = 0;
-		$telnet->put("exit\n");
-		$telnet->close;
-	}
-}
-
-sub get_track_info {
-	return undef unless open_telnet();
-
-	my $line = undef;
-
-	unless (defined($telnet->print("trackinfo"))) {
-		close_telnet();
-		return undef unless open_telnet();
-	}
-
-	my @result = $telnet->waitfor(Match => '/11[123]\|+.+?\|+.+?\|+(?!0\.[0-5][0-9]).*/', Timeout => 5);
-
-	$line = $result[1] if @result;
-	
-	close_telnet();
-	
-	unless($line) {
-		irc_print("Error retrieving status from foobar2000!");
-		return undef;
-	}
-	
-	unless (eval { $line = decode("UTF-8", $line, Encode::FB_CROAK) }) {
-		irc_print("Error: line is not valid UTF-8. Check foo_controlserver's settings.");
-		return undef;
-	}
-	
-	%heap = ();
-
-	my @fields;
-
-	if($line =~ /^11.\|\|\|/ and $line =~ /\|\|\|(.*?)\|\|\|$/) { # proper setting
-		@fields = split(/\|\|\|/, $line);
-	} else {                    # the luser didn't configure it correctly
-		$line =~ s/\|\|\|/\|/g; # fix possible half-configuration
-		@fields = split(/\|/, $line);
-	}
-
-	# Standard settings
-	my $info = {state                  => $fields[0],
-	            playback_time_seconds  => $fields[3],
-	            codec                  => $fields[4],
-	            bitrate                => $fields[5],
-	            'album artist'         => $fields[6],
-	            album                  => $fields[7],
-	            date                   => $fields[8],
-	            genre                  => $fields[9],
-	            tracknumber            => $fields[10],
-	            title                  => $fields[11]};
-	if ($fields[19]) { # 
-		$info->{'artist'}              = $fields[12];
-		$info->{'totaltracks'}         = $fields[13];
-		$info->{'playback_time'}       = $fields[14];
-		$info->{'length'}              = $fields[15];
-		
-		$info->{'_foobar2000_version'} = $fields[16];
-		
-		$info->{'codec_profile'}       = $fields[17];
-
-		$info->{'discnumber'}          = $fields[18];
-		$info->{'totaldiscs'}          = $fields[19];
-	}
-
-	$info->{'isplaying'} = 1;
-	$info->{'ispaused'} = 0;
-	if ($info->{'state'} eq "113") {
-		$info->{'ispaused'} = 1;
-	} elsif ($info->{'state'} eq "112") {
-		$info->{'isplaying'} = 0;
-	}
-	delete $info->{'state'};
-
-	for (keys %$info) {
-		delete $info->{$_} if (defined($info->{$_}) and $info->{$_} eq '?');
-	}
-
-	$info->{'album artist'} = $info->{'artist'} unless defined($info->{'album artist'});
-	$info->{'track artist'} = $info->{'artist'} if (defined($info->{'artist'}) and $info->{'album artist'} ne $info->{'artist'});
-	
-	if (defined($info->{'length'})) {
-		my ($h, $m, $s) = split(/\:/, $info->{'length'});
-		if (defined $s) {
-			$info->{'length_seconds'} = $s + $m * 60 + $h * 3600;
-		} else {
-			$info->{'length_seconds'} = $m + $h * 60;
-		}
-	}
-	
-	if ($info->{'length_seconds'} and $info->{'playback_time_seconds'}) {
-		$info->{'playback_time_remaining_seconds'} = 
-				$info->{'length_seconds'} - $info->{'playback_time_seconds'};
-	}
-
-	for (('playback_time', 'playback_time_remaining')) {
-		unless (defined($info->{$_})) {
-			my $t = $info->{"${_}_seconds"};
-
-			my @u = (0,0);
-			for (my $i = 1; $i >= 0; $i--) {
-				$u[$i] = $t % 60;
-				$t = int($t / 60);
-			}
-			$info->{$_} = sprintf("%s%02d:%02d", $t > 0 ? "$t:" : "", @u[0,1]);
-		}
-	}
-
-	return $info;
-}
-
-sub parse_format {
-	my ($format, $info, $sublevel) = @_;
-	$sublevel = 0 if not defined $sublevel;
-
-	my $output = "";
-
-	$format =~ s/\R//g; # ignore line breaks
-	my @chars = split(//,$format);
-
-	# Language Definition
-
-	# lowercasestring      <== should be parsed as a tag name, makes the expression fail if such tag is not defined
-	# []                   <== brackets allow the parsing inside them to fail
-	# $func(arg1,arg2,...) <== function call (see parse_subfunction for details)
-	# ''                   <== string literal (ignores all parsing)
-	# \(character)         <== literal character
-
-	# Bracket Nesting
-
-	# A bracket returns a defined value only if it has at least one tag or at least one of its embedded brackets return true.
-
-	my @tokens = ();
-	my $tagcount = 0;
-	my $fail = 0;
-
-	my $literal = 0;
-	my $sub = 0;
-	my $func = 0;
-	my $tagmode = 0;
-	my $str = "";
-	my $ignore = 0;
-
-	for(my $i = 0; $i < @chars; $i++ ) { # 1st Pass (Lexical analysis, push into @tokens)
-		if($literal) { # If on literal mode
-			$str .= $chars[$i]; # Simply copy everything as-is until an unescaped ' is found
-			if ($chars[$i] eq "'") {
-				push @tokens, $str;
-				$str = "";
-				$literal = 0;
-			} elsif (not defined($chars[$i+1])) { # This means we ended the string with an uneven number of unescaped 's
-				warn "Malformed: mismatched ': $str";
-				return undef;
-			}
-		} elsif ($sub) { # If on subexpression mode
-			$str .= $chars[$i]; # Copy everything as-is until an unescaped ] is found
-			if ($chars[$i] eq "'") {
-				$ignore = !$ignore;
-			} elsif ($chars[$i] eq "[") { # We must copy any sub-subexpressions inside this sub-expression for recursive evaluation
-				++$sub unless $ignore;
-			} elsif ($chars[$i] eq "]" and !$ignore and --$sub == 0) {
-				push @tokens, $str;
-				$str = "";
-			} elsif (not defined($chars[$i+1])) { # This means we ended the string without $sub being 0
-				warn "Malformed: mismatched [: $str";
-				return undef;
-			}
-		} elsif ($tagmode) { # If on tag mode
-			$str .= $chars[$i]; # Copy tags as-is until any % character is found
-			if ($chars[$i] eq '%') {
-				push @tokens, $str;
-				$str = "";
-				$tagmode = 0;
-			} elsif (not defined($chars[$i+1])) {
-				warn "Malformed: mismatched %: $str";
-				return undef;
-			}
-		} elsif ($func) { # If on function mode
-			$str .= $chars[$i]; # Copy everything until an unescaped ) is found
-			if ($chars[$i] eq "'") {
-				$ignore = !$ignore;
-			} elsif ($chars[$i] eq "(") {
-				$func++ unless $ignore;
-			} elsif ($chars[$i] eq ")" and !$ignore and --$func <= 1) {
-				push @tokens, $str;
-				$str = "";
-				$func = 0;
-			} elsif (not defined($chars[$i+1])) {
-				warn "Malformed: mismatched (: $str";
-				return undef;
-			}
-		} else {
-			if ($chars[$i] eq "'") {
-				push @tokens, "$str" if $str ne ""; # Found an opening quote
-				$str = $chars[$i];
-				$literal = 1; # Enter literal mode
-			} elsif ($chars[$i] eq "[") {
-				push @tokens, "$str" if $str ne ""; # Found a subexpression opener
-				$str = $chars[$i];
-				$sub = 1; # Enter subexpression mode
-			} elsif ($chars[$i] eq "\$") {
-				push @tokens, "$str" if $str ne "";
-				$str = $chars[$i];
-				$func = 1; # Enter subfunction mode
-			} elsif ($chars[$i] eq "%") {
-				push @tokens, "$str" if $str ne ""; # Found a tag name
-				$str = $chars[$i];
-				$tagmode = 1; # Enter tag mode
-			} else {
-				$str .= $chars[$i]; # Copy as a literal
-			}
-		}
-	}
-
-	push @tokens, "$str" if $str ne ""; # Make sure whatever is left from parsing is added as a literal
-
-	foreach my $token (@tokens) { # 2nd Pass, execute tokens
-		if ($token =~ /^'(.*)'$/ or $token =~ /^([^['%\$].*)$/) { # If the token is a literal, then
-			$output .= $token eq "''" ? "'" : $1; # '' means a literal ', otherwise literal contents
-		} elsif ($token =~ /^%(.*)%$/) { # If this is a tag
-			$token = $1;
-			return undef unless defined($info->{$token});
-			$output .= $info->{$token}; # Copy value to output
-		} elsif ($token =~ /^\[(.*)\]$/) { # If the token is a subexpression
-			$token = $1;
-			my $recurse = parse_format($token, $info, $sublevel+1); # Recurse
-			$output .= $recurse if defined($recurse);
-		} elsif ($token =~ /^\$/) { # If the token is a subfunction
-			my $res = parse_subfunction($token, $info, $sublevel);
-			return undef unless defined($res);
-			$output .= $res;
-		} else {
-			warn "Parsing error: $token";
-			return undef;
-		}
-	}
-
-	return $output;
-}
-
-sub build_output {
-	my ($format, $info, $sublevel) = @_;
-	$sublevel = 0 if not defined $sublevel;
-
-	return parse_format($format, $info, $sublevel);
-}
-
-sub parse_subfunction {
-	my ($function, $info, $sublevel) = @_;
-
-	$function =~ /^\$(.*?)\((.*)\)$/;
-
-	my $func = $1;
-
-	my @rawargs = split(//, $2);
-	my @args = ();
-
-	my $ignore = 0;
-	my $str = "";
-	for(my $i = 0; $i < @rawargs; $i++) {
-		if ($rawargs[$i] eq "'") {
-			$ignore = !$ignore;
-		} elsif ($rawargs[$i] eq ",") {
-			unless ($ignore) {
-				push @args, $str;
-				$str = "";
-				++$i;
-			}
-		}
-		$str .= $rawargs[$i] if defined($rawargs[$i]);
-	}
-	push @args, $str;
-
-	for (my $i = 0; $i < @args; $i++) {
-		$args[$i] = parse_format($args[$i], $info, $sublevel+1);
-	}
-
-	if ($func eq "len") {
-		return defined $args[0] ? length($args[0]) : undef;
-	} elsif ($func eq "repeat") {
-		return (defined $args[0] and defined $args[1]) ? ($args[0] x $args[1]) : undef;
-	} elsif ($func eq "trim") {
-		my ($str) = @args;
-		return undef unless defined $str;
-		$str =~ /^\s*+(.*?)\s*+$/;
-		return $1;
-	} elsif ($func eq "put" or $func eq "puts") {
-		my ($var, $val) = @args;
-		return undef unless (defined $var and defined $val);
-		$heap{$var} = $val;
-		return ($func eq "put") ? $val : "";
-	} elsif ($func eq "get") {
-		my ($var) = @args;
-		return undef unless defined $var;
-		return exists $heap{$var} ? $heap{$var} : "";
-	} elsif ($func eq "pad" or $func eq "pad_right" or $func eq "left" or $func eq "cut" or $func eq "padcut" or $func eq "padcut_right") {
-		my ($str, $maxlen, $char) = @args;
-		return undef unless (defined $str and $maxlen);
-		
-		my $pad = ($func eq "pad" or $func eq "pad_right" or $func eq "padcut" or $func eq "padcut_right");
-		my $cut = ($func eq "left" or $func eq "cut" or $func eq "padcut" or $func eq "padcut_right");
-		
-		if ($cut) {
-			$str = substr($str, 0, $maxlen);
-		}
-		if ($pad) {
-			$char = " " unless defined $char and $char ne "";
-			$char = substr($char, 0, 1);
-			$str .= ($char x ($maxlen - length($str)));
-		}
-		return $str;
-	} elsif ($func eq "right") {
-		my ($str, $maxlen) = @args;
-		return undef unless (defined $str and defined $maxlen);
-		return substr($str, -$maxlen);
-	} elsif ($func eq "insert" or $func eq "replace") {
-		my ($haystack, $needle, $pos) = @args;
-		return undef unless (defined($haystack) and defined($needle) and defined($pos));
-		if ($func eq "insert") {
-			return substr($haystack, 0, $pos) . $needle . substr($haystack, $pos);
-		}
-		$needle = quotemeta($needle);
-		$haystack =~ s/$needle/$pos/g;
-		return $haystack;
-	} elsif ($func eq "if" or $func eq "if2") {
-		my ($test, $iftrue, $iffalse);
-		if ($func eq "if") {
-			($test, $iftrue, $iffalse) = @args;
-		} else {
-			($test, $iffalse) = @args;
-			$iftrue = $test;
-		}
-
-		if ($test) {
-			return $iftrue;
-		} else {
-			return $iffalse;
-		}
-	} elsif ($func eq "if3") {
-		foreach (@args) {
-			return $_ if $_;
-		}
-		return undef;
-	} elsif ($func eq "greater") {
-		my ($arg1, $arg2) = @args;
-		return undef unless (defined($arg1) or defined($arg2));
-		return $arg1 unless defined $arg2;
-		return $arg2 unless defined $arg1;
-		return $arg1 if $arg1 >= $arg2;
-		return $arg2;
-	} elsif ($func eq "longer") {
-		my ($arg1, $arg2) = @args;
-		return undef unless (defined($arg1) or defined($arg2));
-		return $arg1 unless defined $arg2;
-		return $arg2 unless defined $arg1;
-		return $arg1 if length($arg1) >= length($arg2);
-		return $arg2;
-	} elsif ($func eq "longest") {
-		return undef unless scalar(@args);
-		my $longest = $_[0];
-		foreach (@args) {
-			next unless defined;
-			$longest = $_ if length($_) > length($longest);
-		}
-		return $longest;
-	} elsif ($func eq "ifgreater" or $func eq "ifequal" or $func eq "iflonger") {
-		my ($arg1, $arg2, $iftrue, $iffalse) = @args;
-
-		unless (defined($arg2)) {
-			return $iftrue if (defined($arg1));
-			return $iffalse;
-		}
-		return $iffalse unless (defined($arg1));
-
-		if ($func eq "iflonger") {
-			return defined($arg1) ? $iftrue : $iffalse unless (defined($arg1) and defined($arg2));
-			return $iftrue if (length($arg1) > length(" " x $arg2));
-		} elsif ($func eq "ifequal") {
-			# Any of the args may not be comparable, return false in that case
-			return $iftrue if (defined($arg1) and defined($arg2));
-			return $iffalse unless (defined($arg1) and defined($arg2));
-			eval { return $iftrue if $arg1 == $arg2 };
-		} else { # ifgreater
-			return defined($arg1) ? $iftrue : $iffalse unless (defined($arg1) and defined($arg2));
-			eval { return $iftrue if $arg1 > $arg2 };
-		}
-		return $iffalse;
-	} elsif ($func eq "abbr") {
-		my ($arg1, $arg2) = (0,0);
-		$arg1 = $args[0];
-		$arg2 = $args[1] if (defined($args[1]));
-		return undef unless (defined $arg1 and $arg2 >= 0);
-
-		if (length($arg1) > $arg2) {
-			my $abbr = "";
-			my @tokens = split(/\s+/, $arg1);
-			foreach my $token (@tokens) {
-				my @chars = split(//, $token);
-				$abbr .= $chars[0];
-			}
-			return $abbr;
-		}
-		return $arg1;
-	} elsif ($func eq "num") {
-		my ($arg1, $arg2) = @args;
-		return undef unless (defined($arg1) and $arg2 > 0);
-		return sprintf("%0${arg2}d", $arg1);
-	} elsif ($func =~ /^(add|sub|mul|div|mod|max|min)$/) {
-		my ($arg1, $arg2) = @args;
-		return undef unless (defined($arg1) and defined($arg2));
-		# Make sure both are numbers. Better way to do this?
-		return undef unless eval { $arg1 != $arg2 or $arg1 == $arg2 };
-		return $arg1 + $arg2 if ($func eq "add");
-		return $arg1 - $arg2 if ($func eq "sub");
-		return $arg1 * $arg2 if ($func eq "mul");
-		return $arg1 / $arg2 if ($func eq "div");
-		return $arg1 % $arg2 if ($func eq "mod");
-		return ($arg1 >= $arg2 ? $arg1 : $arg2) if ($func eq "max");
-		return ($arg1 < $arg2 ? $arg1 : $arg2) if ($func eq "min");
-	} elsif ($func =~ /^(and|or|xor|not)$/) {
-		my ($arg1, $arg2) = @args;
-		$arg1 = 0 unless defined $arg1;
-		$arg2 = 0 unless defined $arg2;
-
-		# Need to give explicit returns to avoid eating on parse_format
-
-		return ($arg1 ? 0 : 1) if ($func eq "not");
-		return (($arg1 && $arg2) ? 1 : 0) if ($func eq "and");
-		return (($arg1 || $arg2) ? 1 : 0) if ($func eq "or");
-		return (($arg1 && !$arg2) ? 1 : ((!$arg1 && $arg2) ? 1 : 0)) if ($func eq "xor");
-	} elsif ($func eq "strcmp" or $func eq "stricmp") {
-		my ($arg1, $arg2) = @args;
-		return undef unless (defined($arg1) and defined($arg2));
-		return ((lc($arg1) eq lc($arg2)) ? 1 : 0) if ($func eq "stricmp");
-		return (($arg1 eq $arg2) ? 1 : 0);
-	} elsif ($func eq "caps") {
-		my ($arg1) = @args;
-		return undef unless defined $arg1;
-		$arg1 =~ s/\b(\S)(\S*)\b/@{[uc($1)]}@{[lc($2)]}/g;
-		return $arg1;
-	} elsif ($func eq "caps2") {
-		my ($arg1) = @args;
-		return undef unless defined $arg1;
-		$arg1 =~ s/\b(\S)/@{[uc($1)]}/g;
-		return $arg1;
-	} elsif ($func eq "lower" or $func eq "upper") {
-		my ($arg1) = @args;
-		return undef unless defined $arg1;
-		return lc($arg1) if $func eq "lower";
-		return uc($arg1);
-	} elsif ($func eq "fix_eol") {
-		my ($meta, $repl) = @args;
-		$repl = " (...)" unless $repl;
-		return undef unless defined($meta);
-		$meta =~ s/\010?\013.*//;
-		return $meta;
-	}
-
-	warn "Unknown or unimplemented function: $function";
-	return undef;
-}
-
-sub get_np_string {
-	my $info = get_track_info();
-	$info->{comment} = $_[0] if $_[0];
-	if (defined($info)) {
-		return build_output($format, $info);
-	}
-	return undef;
-}
-
-sub get_help_string {
-	my $fields;
-	if (HAVE_IRSSI) {
-		$fields = '%%codec%%|||%%bitrate%%|||%%album artist%%|||%%album%%|||%%date%%|||%%genre%%|||%%tracknumber%%|||%%title%%|||%%artist%%|||%%totaltracks%%|||%%playback_time%%|||%%length%%|||%%_foobar2000_version%%|||%%codec_profile%%|||%%discnumber%%|||%%totaldiscs%%';
-	} else {
-		$fields = '%codec%|||%bitrate%|||%album artist%|||%album%|||%date%|||%genre%|||%tracknumber%|||%title%|||%artist%|||%totaltracks%|||%playback_time%|||%length%|||%_foobar2000_version%|||%codec_profile%|||%discnumber%|||%totaldiscs%';
-	}
-	my $help = <<EOF
-Required Plugin: foo_controlserver
-URL: http://www.hydrogenaudio.org/forums/index.php?showtopic=38114
-Required settings: Control Server tab:
-* Server Port: 3333
-* UTF-8 output/input: checked
-* Base delimiter: |||
-Recommended setting:
-* Number of Clients: Some big number like 700
-* Fields: $fields
-
-NOTE: the script only works with either the default or this custom Fields line.
-
-This script can also work via SSH tunneling, by using -R 3333:localhost:3333.
-
-EOF
-;
-	return $help;
-}
-
-sub get_intro_string {
-	my $intro = <<EOF
-\002-----------------------------------------------------------------
-\002foo_spam - prints the currently playing track from foobar2000
-\002Created by Kovensky \(irc.rizon.net #shameimaru\)
-This script requires a properly configured foobar2000.
-Run /foo_help for help setting foobar2000 up.
-\002-----------------------------------------------------------------
-Usage:
-/aud        - prints the playing song as an ACTION
-/np         - alias to /aud
-/foo_help   - explains how to set up foobar2000
-/foo_format - explains how to set up the output format
-\002-----------------------------------------------------------------
-
-EOF
-;
-	return $intro;
-}
-
-sub get_foo_format_help_string {
-	my $help = <<EOF
-Format Definition
-Example: %artist% - [%album% - ]%title%
-
-foo_spam now uses the same syntax as foobar2000 (title format), however only
-a subset of it is currently implemented. To see the list of supported
-tags, use /foo_tags. To see the list of supported functions, use
-/foo_funcs.
-
-To change the format, you can use:
- * Irssi: /set foo_format <new format> (use /set -default to reset)
- * X-Chat: /set_foo_format <new format> (use /set_foo_format default to reset)
- * WeeChat: /set plugins.var.foo_spam.format <new format> (use /unset to reset)
-You can also edit the script and change the value of \$default_format, in case
-you use an unsupported client.
-
-Default: $default_format
-
-EOF
-;
-	return $help;
-}
-
-sub get_taglist_string {
-	my $list = <<EOF
-List of available tags (refer to foobar2000's documentation for their meanings):
- - %isplaying%, %ispaused%, %_foobar2000_version%
- - %playback_time%, %playback_time_remaining%, %length% (plus the _seconds variants)
- - %artist%, %album artist%, %track artist%, %album%, %title%, %genre%
- - %date%, %discnumber%, %totaldiscs%, %tracknumber%, %totaltracks%
- - %codec%, %bitrate%, %codec_profile%
-The %comment% tag is set by foo_spam itself and it contains all arguments that the user gives to /aud in a single string.
-EOF
-;
-	return $list;
-}
-
-sub get_funclist_string {
-	my $list = <<'EOF'
-List of available functions (refer to foobar2000's documentation for their meanings):
- - $if(X,Y,Z), $if2(X,Y), $if3(X,Y,Z,...), $ifgreater(A,B,C,D), $iflonger(A,B,C,D), $ifequal(A,B,C,D)
- - $and(X,Y), $or(X,Y), $xor(X,Y), $not(X)
- - $strcmp(X,Y), $stricmp(X,Y), $len(X), $num(X,Y)
- - $greater(X,Y), $longer(X,Y), $longest(A,B,C,...)
- - $caps(X), $caps2(X), $lower(X), $upper(X)
- - $trim(A), $pad(X,Y), $pad_right(X,Y), $pad(X,Y,Z), $pad_right(X,Y,Z), $left(X,Y), $cut(X,Y), $padcut(X,Y), $padcut_right(X,Y), $right(X,Y)
- - $insert(A,B,N), $replace(A,B,C), $repeat(X,N)
- - $abbr(X), $abbr(X,Y)
- - $add(X,Y), $sub(X,Y), $mul(X,Y), $div(X,Y), $mod(X,Y), $min(X,Y), $max(X,Y)
- - $put(name,text), $puts(name,text), $get(name)
-EOF
-;
-	return $list;
-}
-
-if (HAVE_IRSSI) {
-	*print_now_playing = sub {
-		my ($data, $server, $witem) = @_;
-		my $str = get_np_string(decode("UTF-8", $data));
-		if (defined($str)) {
-			if ($witem && ($witem->{type} eq "CHANNEL" ||
-				$witem->{type} eq "QUERY")) {
-				$witem->command(encode_utf8("me $str"));
-			}
-		}
-	};
-
-	*print_foo_help = sub{
-		Irssi::print(get_help_string());
-	};
-
-	*print_foo_format_help = sub {
-		my $help = get_foo_format_help_string();
-		$help =~ s/%/%%/g;
-		Irssi::print($help);
-	};
-
-	*irc_print = sub {
-		Irssi::print($_[0]);
-	};
-
-	*print_foo_tags = sub {
-		my $help = get_foo_taglist_string();
-		$help =~ s/%/%%/g;
-		Irssi::print($help);
-	};
-	
-	*print_foo_funcs = sub {
-		Irssi::print(get_funclist_string());
-	};
-
-	Irssi::settings_add_str("foo_spam", "foo_format", $format);
-	$format = Irssi::settings_get_str("foo_format");
-
-	Irssi::command_bind('aud', 'print_now_playing');
-	Irssi::command_bind('np', 'print_now_playing');
-	Irssi::command_bind('foo_help', 'print_foo_help');
-	Irssi::command_bind('foo_format','print_foo_format_help');
-	Irssi::command_bind('foo_tags','print_foo_tags');
-	Irssi::command_bind('foo_funcs','print_foo_funcs');
-} elsif (HAVE_XCHAT) {
-	*print_now_playing = sub {
-		my $str = get_np_string($_[0][1] ? $_[1][1] : undef);
-		if (defined($str)) {
-			Xchat::command(encode_utf8("me $str"));
-		}
-		return Xchat::EAT_ALL();
-	};
-
-	*print_foo_help = sub {
-		Xchat::print(get_help_string());
-		return Xchat::EAT_ALL();
-	};
-
-	*irc_print = sub {
-		Xchat::print(@_);
-	};
-
-	*set_foo_format = sub {
-		if (defined($_[0][1])) {
-			open($setting_file, ">", Xchat::get_info('xchatdir') . "/foo_spam.conf");
-			if ($_[0][1] eq "default") {
-				$format = $default_format;
-			} else {
-				$format = $_[1][1];
-			}
-			Xchat::print("Changed format to $format\n");
-			if (defined($setting_file)) {
-				print $setting_file $format;
-				close($setting_file);
-			} else {
-				Xchat::print("Failed to save settings! Error: $!");
-			}
-		} else {
-			Xchat::print("Current format: $format\n");
-		}
-		return Xchat::EAT_ALL();
-	};
-	if (defined(*set_foo_format)) {} # Silence a warning
-
-	*print_foo_format_help = sub {
-		Xchat::print(get_foo_format_help_string());
-		return Xchat::EAT_ALL();
-	};
-
-	*print_foo_tags = sub {
-		Xchat::print(get_taglist_string());
-		return Xchat::EAT_ALL();
-	};
-	
-	*print_foo_funcs = sub {
-		Xchat::print(get_funclist_string());
-		return Xchat::EAT_ALL();
-	};
-
-	if (open($setting_file, "<", Xchat::get_info('xchatdir') . "/foo_spam.conf")) {
-		my $line = <$setting_file>;
-		chomp $line;
-		$format = $line if (defined($line) and $line ne "");
-		close($setting_file);
-	}
-
-	Xchat::hook_command("np","print_now_playing", {help => "alias to /aud"});
-	Xchat::hook_command("aud","print_now_playing", {help => "prints your currently playing song on foobar2000 on an ACTION"});
-	Xchat::hook_command("foo_help","print_foo_help", {help => "explains how to set up foobar2000"});
-	Xchat::hook_command("set_foo_format","set_foo_format", {help => "displays or changes the current format string"});
-	Xchat::hook_command("foo_format","print_foo_format_help", {help => "explains how to configure the format string"});
-	Xchat::hook_command("foo_tags","print_foo_tags", {help => "lists all available tags"});
-	Xchat::hook_command('foo_funcs','print_foo_funcs', {help => "lists all available functions"});
-} elsif (HAVE_WEECH) {
-	*print_now_playing = sub {
-		my ($data, $buffer, @args) = @_;
-		$format = weechat::config_get_plugin("format");
-		my $str = get_np_string($args[0] ? decode("UTF-8", join(' ', @args)) : undef);
-		if (defined($str)) {
-			weechat::command($buffer, encode_utf8("/me $str"));
-		}
-		return weechat::WEECHAT_RC_OK_EAT();
-	};
-
-	*irc_print = sub {
-		weechat::print('', shift);
-	};
-
-	*print_foo_help = sub {
-		irc_print(get_help_string());
-		return weechat::WEECHAT_RC_OK_EAT();
-	};
-
-	*print_foo_format_help = sub {
-		irc_print(get_foo_format_help_string());
-		return weechat::WEECHAT_RC_OK_EAT();
-	};
-
-	*print_foo_tags = sub {
-		irc_print(get_taglist_string());
-		return weechat::WEECHAT_RC_OK_EAT();
-	};
-	
-	*print_foo_funcs = sub {
-		irc_print(get_funclist_string());
-		return Xchat::WEECHAT_RC_OK_EAT();
-	};
-
-	unless (weechat::config_is_set_plugin("format")) {
-		weechat::config_set_plugin("format", $default_format);
-	}
-
-	weechat::hook_command('np', 'alias to /aud', '', '', '%(nicks)', 'print_now_playing', '');
-	weechat::hook_command('aud', 'prints your currently playing song on foobar2000 on an ACTION', '', '', '%(nicks)', 'print_now_playing', '');
-	weechat::hook_command('foo_help', 'explains how to set up foobar2000', '', '', '', 'print_foo_help', '');
-	weechat::hook_command('foo_format', 'explains how to configure the format string', '', '', '', 'print_foo_format_help', '');
-	weechat::hook_command('foo_tags', 'lists all available tags', '', '', '', 'print_foo_tags', '');
-	weechat::hook_command('foo_funcs', 'lists all available functions', '', '', '', 'print_foo_funcs', '');
-} else {
-	$| = 1;
-	binmode (STDERR, ":encoding(utf-8)");
-	binmode (STDOUT, ":encoding(utf-8)");
-	*irc_print = sub {
-		print (STDERR "@_\n") if @_;
-	};
-	$format = join(" ", @ARGV) if $ARGV[0];
-	my $np = get_np_string();
-	print "$np\n" if $np;
-}
-
-if (HAVE_XCHAT or HAVE_IRSSI or HAVE_WEECH) {
-	irc_print(get_intro_string());
-}
-
