text: # TinyUrl, version 3.8, for weechat version 0.3.0 or later
  #
  #   Listens to all channels for long URLs, and submits them to ln-s.net or
  #   tinyurl.com for easier links.
  #
  # Usage:
  #
  #   By default, any URL longer than 30 characters in length is grabbed,
  #   submitted to a service, and printed in the channel for your eyes only.  For
  #   example, you may see something like this:
  #
  # [11:21] <@lack> http://www.cbc.ca/story/canada/national/2005/11/12/mcdona
  #                 lds-051112.html?ref=rss 
  # [11:21] -P- [AKA] http://tinyurl.com/9dthl
  #
  #   Now you can just cut&paste the easier, shorter URL into your favourite
  #   browser.
  # 
  #   If you want to be extra-helpful (or annoying) to certain channels you
  #   are in, you can actually have the script say the tinyurl.com equivalent
  #   of all long URLs, by adding the channel to the 'activechans' list.  In
  #   that case, everyone in the channel would see the following:
  #
  # [11:25] <testuser> http://www.cbc.ca/story/canada/national/2005/11/12/mcdona
  #                    lds-051112.html?ref=rss 
  # [11:25] <@lack> [AKA] http://tinyurl.com/9dthl
  #
  # Configuration:
  #
  #   Run '/help tinyurl' for the actual usage for setting these options:
  #
  #   activechans
  #     A comma-delimited list of channels you will actually "say" the
  #     tinyurl in.  By default the list is empty.  Be warned, some channels
  #     won't appreciate extra help (or 'noise' as they like to call it), and
  #     some channels already have bots that do this.  Please only enable
  #     this in channels where the ops have given you permission.
  #
  #   urllength
  #     An integer, default value 30.  Any URL this long or longer will
  #     trigger a tinyurl event.
  #
  #   printall
  #     Either "on" or "off", default "on".  When ON, you will see the
  #     tinyurl printed in your window for any channels not in your
  #     activechans list.  When OFF, you will not see any tinyurls except in
  #     your activechans list.
  #
  #   service
  #     Either "tinyurl" or "ln-s".  tinyurl is the default.
  #
  # Requirements:
  #
  #  - Designed to run with weechat version 0.3.0 or better.
  #      http://weechat.flashtux.org/
  #
  #  - Requires that 'curl' is in the path (tested with curl 7.15.0).
  #      http://curl.haxx.se/
  #
  # Copyright (C) 2005 Jim Ramsay <i.am@jimramsay.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 Street, Fifth Floor, Boston, MA  02110-1301,
  # USA.
  #
  # Changelog:
  #
  # Version 3.8, 12 December, 2009
  #   Update WeeChat site
  #   by FlashCode <flashcode@flashtux.org>
  #
  # Version 3.7, 2 May, 2009
  #   Sync with last API changes
  #   by FlashCode <flashcode@flashtux.org>
  #
  # Version 3.6, 12 March, 2009
  #   Conversion to WeeChat 0.3.0+
  #   by FlashCode <flashcode@flashtux.org>
  #
  # Version 3.5d, 19 September, 2008
  #   Updated with recent api changes
  #   by Chris Hills <chaz@chaz6.com>
  #
  # Version 3.5c, 18 July, 2008
  #   Send the url to the correct channel in activechans
  #   by Chris Hills <chaz@chaz6.com>
  #
  # Version 3.5b, 18 July, 2008
  #   Fixed a few prnt functional calls
  #   by Chris Hills <chaz@chaz6.com>
  #
  # Version 3.5a, 18 July, 2008
  #   Updated for the new script api in weechat 0.3.0-dev
  #   by Chris Hills <chaz@chaz6.com>
  #   Updated to use the tinyurl api instead of screen-scraping
  #   by Chris Hills <chaz@chaz6.com>
  #
  # Version 3.5, June 3, 2008
  #   Added ln-s.net support
  #   Turned off some ugly verbose output unless requested
  #
  # Version 3.4, June 3, 2008
  #   tinyurl.com changed their output page slightly, and broke our parsing
  #
  # Version 3.3, July 4, 2006
  #   Catches possible error in os.waitpid
  #   Properly prints tinyurls in query windows
  #
  # Version 3.2, June 15, 2006
  #   Multiple configuration bugfixes, pointed out by Stalwart on #weechat.
  #
  # Version 3.1, June 15, 2006
  #   Now kills any leftover curl processes when the script is unloaded.
  #   Thanks again to kolter for the great idea!
  #   Also cleaned up /tinyurl command, added comletion_template, updated
  #   help text, improved option parsing logic, etc.
  #
  # Version 3.0, June 15, 2006
  #   Fixes "tinyurl script sometimes makes weechat freeze" issue by using
  #   the new timer handlers available in Weechat 0.1.9
  #   Also includes URL detection fix from Raimund Specht
  #   <raimund@spemaus.de>.
  #
  # Version 2.0, Dec 13, 2005
  #   Also catches https, ftp, and ftps URLs, thanks to kolter for the
  #   suggestion!
  #
  # Version 1.1, Dec 2, 2005
  #   Fixed undefined 'urlend' thanks to kolter@irc.freenode.org#weechat
  #
  # TODO:
  #
  # - Handle outgoing messages and replace long urls with the tinyurl
  #   equivalent automatically.
  # - On load, check that 'curl' is installed, and fail if not.
  #
  
  import os, tempfile, re
  try:
  	import urllib
  except:
  	raise ImportError("You need to reload the python plugin to reload urllib")
  import weechat
  
  class TryAgain(UserWarning):
  	def __init__(self, message):
  		super(UserWarning, self).__init__(message)
  
  # Register with weechat
  weechat.register( "TinyUrl", "Jim Ramsay", "3.8", "GPL", "Waits for URLs and sends them to 'tinyurl' for you", "tinyurlShutdown", "" )
  
  # Global variables
  tinyurlParams = ("urllength","activechans","printall","service","debug")
  tinyurlProcessList = {}
  
  # Set default settings values:
  if weechat.config_get_plugin('urllength') == "":
  	weechat.config_set_plugin('urllength', "30")
  if not weechat.config_get_plugin('printall') in ("on", "off"):
  	weechat.config_set_plugin('printall', "on")
  if not weechat.config_get_plugin('service') in ("tinyurl", "ln-s"):
  	weechat.config_set_plugin('service', "tinyurl")
  if not weechat.config_get_plugin('debug') in ("on", "off"):
  	weechat.config_set_plugin('debug', "off")
  
  # Start the timer thread and register handlers
  weechat.hook_timer( 1000, 0, 0, "tinyurlCheckComplete", "" )
  weechat.hook_signal("*,irc_in_privmsg", "tinyurlHandleMessage", "")
  weechat.hook_command("tinyurl", "Sets/Gets 'tinyurl' settings.", "urllength|activechans|printall|service|debug", \
  	"[<variable> [[=] <value>]]",
  """When run without arguments, displays all tinyurl settings
  
  <variable> : Sets or displays a single tinyurl setting. One of:
      activechans [[=] #chan1[,#chan2...]]
          List of channels where others will see your tinyurls.
          Default: None
      urllength [[=] length]
          Will not create tinyurls for any URLs shorter than this.
          Default: 30
      printall [[=] on|off]
          When off, will not display private tinyurls, just those
          displayed publicly in your "active channels"
          Default: on
      service [[=] tinyurl|ln-s]
          Sets the service used for shortening the URL.
          Default: tinyurl
      debug [[=] on|off]
          Creates extra debug output in the server window when on.
          Default: off""",
  	"tinyurlMain", ""
      )
  
  def tinyurlShutdown():
  	"""Cleanup - Kills any leftover child processes"""
  	if len(tinyurlProcessList.keys()) > 0:
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Cleaning up unfinished processes:" )
  		for pid in tinyurlProcessList.keys():
  			weechat.prnt( weechat.buffer_search("",""), "	Process %d" % pid )
  			try:
  				os.kill(pid, 9)
  				os.waitpid( pid, 0 )
  			except:
  				weechat.prnt( weechat.buffer_search("",""), "		Cleanup failed, skipping" )
  	return weechat.WEECHAT_RC_OK
  	
  def tinyurlGet( name = "" ):
  	"""Gets a variable value"""
  	if name == "":
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Get all:" )
  		for name in tinyurlParams:
  			weechat.prnt( weechat.buffer_search("",""), "	%s = %s" % (name, weechat.config_get_plugin(name)) )
  	else:
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Get:" )
  		if name in tinyurlParams:
  			weechat.prnt( weechat.buffer_search("",""), "	%s = %s" % (name, weechat.config_get_plugin(name)) )
  		else:
  			weechat.prnt( weechat.buffer_search("",""), "	Unknown parameter \"%s\", try '/help tinyurl'" % name )
  	return
  
  def tinyurlSet( name, value ):
  	"""Sets a variable value"""
  	if value == "":
  		tinyurlGet( name )
  	else:
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Set:" )
  		if name in tinyurlParams:
  			if name == "printall" or name == "debug":
  				if value == "0" or value.lower() == "no" or value.lower() == "off":
  					value = "off"
  				elif value == "1" or value.lower() == "yes" or value.lower() == "on":
  					value = "on"
  				else:
  					weechat.prnt( weechat.buffer_search("",""), "	%s must be one of 'on' or 'off'" % name )
  					weechat.prnt( weechat.buffer_search("",""), "	value = '%s'" % value )
  					return
  			elif name == "service":
  				if value.lower() in ("tinyurl", "ln-s"):
  					value = value.lower()
  				else:
  					weechat.prnt( weechat.buffer_search("",""), "	service must be one of 'tinyurl' or 'ln-s'" )
  					weechat.prnt( weechat.buffer_search("",""), "	value = '%s'" % value )
  					return
  			elif name == "urllength":
  				try:
  					v = int(value)
  					if v < 0 or v > 100:
  						weechat.prnt( weechat.buffer_search("",""), "	urllength must be between 0 and 100" )
  						weechat.prnt( weechat.buffer_search("",""), "	value = '%s'" % value )
  						return
  				except:
  					weechat.prnt( weechat.buffer_search("",""), "	urllength must be a valid integer" )
  					weechat.prnt( weechat.buffer_search("",""), "	value = '%s'" % value )
  					return
  			elif name == "activechans":
  				vs = re.split(", |,| ", value)
  				values = []
  				for v in vs:
  					if v.startswith("#"):
  						values.append(v)
  						value = ",".join(values)
  			weechat.config_set_plugin(name, value)
  			weechat.prnt( weechat.buffer_search("",""), "	%s = %s" % (name, weechat.config_get_plugin(name)) )
  		else:
  			weechat.prnt( weechat.buffer_search("",""), "	Unknown parameter \'%s\'" % name )
  	return
  
  def tinyurlMain( data, buffer, args ):
  	"""Main handler for the /tinyurl command"""
  	args = args.split( " " )
  	while '' in args:
  		args.remove('')
  	while ' ' in args:
  		args.remove(' ')
  	if len(args) == 0:
  		tinyurlGet()
  	else:
  		name = args[0]
  		value = ""
  		if len(args) > 1:
  			if args[1] == "=":
  				value = " ".join(args[2:])
  			else:
  				value = " ".join(args[1:])
  			tinyurlSet( args[0], value )
  		else:
  			tinyurlGet( name )
  	return weechat.WEECHAT_RC_OK
  
  def tinyurlGetUrl( url, channel, server ):
  	"""Starts a background process which will query the appropriate service and
  	put the result in a file that the timer function 'tinyurlCheck' will find and
  	parse."""
  	global tinyurlProcessList
  	handle, filename = tempfile.mkstemp( prefix="weechat-tinyurl.py-" )
  	os.close(handle)
  	service = weechat.config_get_plugin('service')
  	if service == "tinyurl":
  		cmd = ("curl -d url=%s http://tinyurl.com/api-create.php --stderr /dev/null -o %s" % \
  			 (urllib.quote(url), filename)).split()
  	else:
  		cmd = ("curl http://ln-s.net/home/api.jsp?url=%s --stderr /dev/null -o %s" % \
  			 (urllib.quote(url), filename)).split()
  	try:
  		pid = os.spawnvp( os.P_NOWAIT, cmd[0], cmd )
  		if weechat.config_get_plugin('debug') == "on":
  			weechat.prnt( weechat.buffer_search(server,""), "Setting ProcessList[%d] to (%s, %s, %s)" % \
  				(pid, filename, channel, server))
  		tinyurlProcessList[pid] = (filename, url, service, channel, server)
  	except Exception, e:
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Error: Could not spawn curl: %s" % (e) )
  
  def parseTinyurl( file ):
  	turl = None
  	for line in file:
  		if( line.startswith("http://tinyurl.com") ):
  			turl = line
  			break
  	if turl is None:
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Error: Unrecognized response from server" )
  		weechat.prnt( weechat.buffer_search("",""), "          Maybe tinyurl.com changed their format again." )
  		weechat.prnt( weechat.buffer_search("",""), "          Try '/tinyurl service ln-s' to use ln-s.net instead" )
  	return turl
  
  def parseLns( file ):
  	turl = None
  	for line in file:
  		(code, message) = line.split(" ", 2)
  		if code == "200":
  			return message.rstrip()
  		elif code == "503":
  			# Try again, respawn curl
  			weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Warning: ln-s.net is busy, trying again shortly" )
  			raise TryAgain(line)
  		else:
  			weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Error: Error response from server: %s" % (line) )
  			return None
  
  def tinyurlParsefile( filename, service ):
  	"""Parses the given HTML file and pulls out the tinyurl."""
  	turl = None
  	try:
  		html = open(filename, "r")
  		if service == "tinyurl":
  			turl = parseTinyurl(html)
  		else:
  			turl = parseLns(html)
  		html.close()
  	except Exception, e:
  		weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Error: Could not open result file %s: %s" % (filename, e) )
  	return turl
  
  def tinyurlPrint( original, url, channel, server ):
  	"""Prints the new tinyurl either to just you, or to the whole channel"""
  	where = "Unknown"
  	locstart = original.find("//")
  	if locstart > -1:
  		locend = original.find("/", locstart + 2)
  		if locend > -1:
  			where = original[locstart + 2:locend]
  	activeChans = weechat.config_get_plugin('activechans').split(',')
  	if channel in activeChans:
  		weechat.command( weechat.buffer_search("irc",server + "." + channel), "/msg %s [AKA] %s" % (channel, url) )
  	else:
  		if weechat.config_get_plugin('debug') == "on":
  			weechat.prnt( weechat.buffer_search("irc",server + "." + channel), "Printing url to channel '%s', server '%s'" % \
  				(channel, server) )
  		weechat.prnt( weechat.buffer_search("irc",server + "." + channel), "[AKA] %s (%s)" % (url, where) )
  
  def tinyurlFindUrlstart( msg, start = 0 ):
  	"""Finds the beginnings of URLs"""
  	index = -1
  	if start < 0 or start >= len(msg):
  		return index
  	for prefix in ( "http://", "https://", "ftp://", "ftps://" ):
  		index = msg.find( prefix, start )
  		if index > -1:
  			break
  	return index 
  
  def tinyurlFindUrlend( msg, urlstart ):
  	"""Finds the ends of URLs (Strips following punctuation)"""
  	m = msg[urlstart:]
  	index = m.find( " " )
  	if index == -1:
  		index = len(m)
  	while msg[index-1] in ( "?", ".", "!" ):
  		index -= 1
  	return index + urlstart
  
  def tinyurlCheckComplete(data, remaining_calls):
  	"""The periodic poll of all waiting processes"""
  	global tinyurlProcessList
  	for pid in tinyurlProcessList.keys():
  		(filename, url, service, channel, server) = tinyurlProcessList[pid]
  		try:
  			(p, er) = os.waitpid( pid, os.WNOHANG )
  			if p != 0:
  				if er == 0:
  					try:
  						tinyurl = tinyurlParsefile(filename, service)
  						if tinyurl is not None:
  							tinyurlPrint( url, tinyurl, channel, server )
  					except TryAgain:
  						tinyurlGetUrl(url, channel, server)
  				else:
  					weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Error: 'curl' did not run properly" )
  				os.unlink(filename)
  				del tinyurlProcessList[pid]
  		except OSError, e:
  			weechat.prnt( weechat.buffer_search("",""), "-TinyUrl- Error: 'curl' process not found: %s" % (e) )
  			os.unlink(filename)
  			del tinyurlProcessList[pid]
  	return weechat.WEECHAT_RC_OK
  
  def tinyurlHandleMessage( data, signal, signal_data ):
  	"""Handles IRC PRIVMSG and checks for URLs"""
  	(server,sig) = signal.split(",",1)
  	maxlen = int(weechat.config_get_plugin( "urllength" ))
  	activeChans = weechat.config_get_plugin('activechans').split(',')
  	onlyActiveChans = weechat.config_get_plugin('printall') == "off"
  	(source, type, channel, msg) = signal_data.split(" ", 3)
  	if onlyActiveChans and channel not in activeChans:
  		return weechat.WEECHAT_RC_OK
  	if not channel.startswith("#"):
  		channel = source.split("!", 2)[0][1:]
  	urlstart = tinyurlFindUrlstart( msg )
  	while urlstart > -1 and urlstart is not None:
  		urlend = tinyurlFindUrlend( msg, urlstart )
  		url = msg[urlstart:urlend]
  		if len(url) >= maxlen:
  			tinyurlGetUrl(url, channel, server)
  		# Check for more URLs
  		urlstart = tinyurlFindUrlstart( msg, urlend+1 )
  	return weechat.WEECHAT_RC_OK
  