Location: PHPKode > projects > Nzbirc > nzbirc-1.4/modules/hella.php
<?php

/**************************************************
 * NZBirc v1
 * Copyright (c) 2006 Harry Bragg
 * tiberious.org
 * Module: hella
 **************************************************
 *
 * Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

class Net_SmartIRC_module_hella extends Net_SmartIRC_module_base
{

	// default module variable
	var $name = 'hella';
	var $version = 'v0.2';
	var $description = 'interfaces with hellanzb';
	
	/**
	 * HellaNZB module
	 *
	 * STARTCONFIG	 
	 * @var array publicCommand   - Lists the commands that trigger this module, and return publicly
	 * @var array privateCommand  - Lists the commands that trigger this module privatly (notice you)
	 * @var array xmlrpc
	 * @var     string hostname   - HellaNZB XML-RPC hostname
	 * @var     int port		  - HellaNZB XML-RPC port
	 * @var	    string password   - HellaNZB XML-RPC password
	 * @var array list
	 * @var     int compactLimit  - Number of results in a list, to use the compact view method (many results on one line)
	 * @var     int positionRange - When listing a particular position, this defines how many items to list around the item you are looking for
	 * @var int moveTimerMax      - Number of minutes to attempt to move an item in the queue (for when force/next adding)
	 * ENDCONFIG
	 */  
	var $_config = array(
		'publicCommand' => array( '^hella', '^!hella' ),
		'privateCommand' => array( '^@hella' ),
		'xmlrpc' => array(
			'hostname' => 'hostname',
			'port' => 8760,
			'password' => 'password' ),
		'list' => array(
			'compactLimit' => 6,
			'positionRange' => 3
			),
		'moveTimerMax' => 20
	);
	
	var $_template = array(
		'status' => array(
			'Hellanzb {version}: up {uptime} | downloaded {total_dl_nzbs} nzbs, {total_dl_files} files ({total_dl_mb}) - {status} [{queued_mb}, ETA: {eta} ({percent_complete}%)]',
			'Downloading: {downloading}, Processing: {processing}, Queued: {queuedItems}'
			),
		'downloading' => '[{id}:{msgid}] {nzbName}',
		'processing' => '[{id}:{msgid}] {nzbName}',
		'queuedItems' => '{total} ({size}{time})',
		'listShort' => '{listItemShort}',
		'listItem' => '({queuePos}) [{id}:{msgid}] {nzbName}{listItemInfo}',
		'listItemShort' => '({queuePos}) [{id}:{msgid}] {nzbName}',
		'listItemInfo' => ' ({user}{queued}{size}{category}{age})',
		'listEmpty' => 'No items found in the queue {search}',
		'paused' => 'Download paused for {pauseLength}',
		'unpaused' => 'Downloads have been resumed',
		'force' => 'Report: {downloading} forced to the front of the queue',
		'next' => 'Report: {downloading} moved to next in the queue',
		'last' => 'Report: {downloading} moved to last in the queue',
		'move' => 'Report: {downloading} moved to position: {position}',
		'newrate' => 'Maxrate set to: {maxrate} kb/s',
		'currentrate' => 'Current maxrate: {maxrate} kb/s',
		'error' => '**Error:** {errormsg}',
		'commandList' => 'List of commands available for HellaNZB | type {trigger} help command, for more information on each command',
		'help' => '{command} - {description}',
		'helpList' => array(
			'Commands available for HellaNZB: {helpCommands}',
			'Type: {trigger} {subTrigger} command, for more information on each command'
			),
		'cancel' => 'Current download canceled',
		'clear' => 'Queue list cleared',
		'del' => 'Item: {listItem} deleted from the queue',
		'rateAdd' => 'Added a rate schedule entry at: {time} setting rate: {rate} kb/s',
		'rateList' => 'Current schedule rate changes: {rateListTime}',
		'rateListTime' => '{time}: {rate} kb/s',
		'rateDel' => 'Deleted the rate schedule entry at: {time}'
	);
	
	var $_def = array(
		'regex' => array(
			'listFlags' => array(
				'pos' => '/p(?:os)?:(\d+)/i',
				'user' => '/u(?:ser)?:(\w+)/i',
				'limit' => '/l(?:imit)?:(\d+)/i',
				'top' => '/((^(top|front|start)$)|(p(?:os)?:(top|front|start)))/i',
				'end' => '/((^(end|last|back)$)|(p(?:os)?:(end|last|back)))/i',
				'id' => '/i(?:d)?:(\d+)/i'
				),
			'itemAge' => '/(-?\d+)\s?(s|m|h|d|w)/i',
			'moveID' => '/(.+)\s(\d+)$/i',
			'rateAdd' => '/^(\d+) (.+)$/', 
			)
		);
	
	var $_help = array(
		'status' => array(
			'command' => '{trigger} status',
			'description' => 'Get the current status of hellanzb',
			'longDescription' => 'Get the current status of hellanzb'
			),
		'list' => array(
			'command' => '{trigger} list [query] [p:position] [u:user] [l:limit] [i:id]',
			'description' => 'list items in the current list, __query__ can be hellanzb\'s ID, NewzBin\'s ID, or part of the name',
			'longDescription' => array(
				'list items in the current list, __query__ can be hellanzb\'s ID, NewzBin\'s ID, part of the name, or match a particular keyword',
				'keywords: **top**,**front**,**start** relate to the beginning of the queue, **end**,**last**,**back** relate to the end of the queue',
				'p/pos:__position__ - list items around the __position__', 
				'u/user:__user__ - List the items the __user__ has queued',
				'l/limit:__limit__ - Change the number of results returned to __limit__', 
				'i/id:__id__ - List the item with the hellanzb __id__'
				)
			),
		'help' => array(
			'command' => '{trigger} help [command]',
			'description' => 'Display the available commands, if a __command__ is specified, then extra information is provided',
			'longDescription' => 'Display the available commands, if a __command__ is specified, then extra information is provided'
			),
		'commands' => array(
			'command' => '{trigger} commands',
			'description' => 'List the available commands with their descriptions',
			'longDescription' => 'List the available commands with their descriptions'
			),
		'pause' => array(
			'command' => '{trigger} pause [duration]',
			'description' => 'Pause the downloads, if a __duration__ is specified (in format Xh Ym etc) then it will unpaused after that time, Default: 1 hour',
			'longDescription' => 'Pause the downloads, if a __duration__ is specified (in format Xh Ym etc) then it will unpaused after that time, Default: 1 hour'
			),
		'continue' => 'unpause',
		'resume' => 'unpause',
		'unpause' => array(
			'command' => '{trigger} unpause/continue/resume',
			'description' => 'Resume downloading',
			'longDescription' => 'Resume downloading'
			),
		'force' => array(
			'command' => '{trigger} force query',
			'description' => 'Forces an item in the queue, and immediatly downloads the item. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item',
			'longDescription' => 'Forces an item in the queue, and immediatly downloads the item. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item'
			),
		'front' => 'next',
		'next' => array(
			'command' => '{trigger} next/front query',
			'description' => 'Moves an item in the queue, to the front in the queue. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item',
			'longDescription' => 'Moves an item in the queue, to the front in the queue. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item'
			),
		'end' => 'last',
		'last' => array(
			'command' => '{trigger} last/end query',
			'description' => 'Moves an item in the queue, to the back of the queue. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item',
			'longDescription' => 'Moves an item in the queue, to the back of the queue. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item'
			),
		'move' => array(
			'command' => '{trigger} move query position',
			'description' => 'Moves the item defined in __query__ to __position__ in the queue. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item',
			'longDescription' => 'Moves the item defined in __query__ to __position__ in the queue. __query__ can be a set of hellanzb IDs or newzbin IDs, or part of the name of an item'
			),
		'maxrate' => 'rate',
		'rate' => array(
			'command' => '{trigger} rate/maxrate [newrate]',
			'description' => 'Set the maximum rate of hellanzb in kb/s. A value of 0 means unlimited. For rate scheduling use {trigger} help rate add/list/del',
			'longDescription' => 'Set the maximum rate of hellanzb in kb/s. A value of 0 means unlimited. For rate scheduling use {trigger} help rate add/list/del'
			),
		'maxrate add' => 'rate add',
		'rate add' => array(
			'command' => '{trigger} rate add speed time',
			'description' => 'Add a new entry into the rate scheduler, these will automatically alter the __speed__ hellanzb downloads at, at different times of the day, __time__ is in stardard time formats (ie: 09:00pm)',
			'longDescription' => 'Add a new entry into the rate scheduler, these will automatically alter the __speed__ hellanzb downloads at, at different times of the day, __time__ is in stardard time formats (ie: 09:00pm)'
			),
		'maxrate list' => 'rate list',
		'rate list' => array(
			'command' => '{trigger} rate list',
			'description' => 'List the current rate scheduler entries',
			'longDescription' => 'List the current rate scheduler entries'
			),
		'maxrate delete' => 'rate del',
		'rate delete' => 'rate del',
		'maxrate del' => 'rate del',
		'rate del' => array(
			'command' => '{trigger} rate del time',
			'description' => 'Delete an entry from the rate scheduler, that matches __time__',
			'longDescription' => 'Delete an entry from the rate scheduler, that matches __time__'
			),
		'cancel' => array(
			'command' => '{trigger} cancel',
			'description' => 'Cancel the current nzb being downloaded',
			'longDescription' => 'Cancel the current nzb being downloaded'
			)
		);
	
	/**
	 * Error store
	 */
	var $_error;
		
	function _registerCommands()
	{
	 	global $irc;
	 	
		$this->_setHandler( '^status', $this, 'botStatus' );
		$this->_setHandler( '^list', $this, 'botList' );
		
		$this->_setHandler( '^pause', $this, 'botPause' );
		$this->_setHandler( '^(unpause|continue|resume)', $this, 'botUnPause' );
		$this->_setHandler( '^(force|next|front|last|end|move) ', $this, 'botMove' );
		
		$this->_setHandler( '^(max)?rate( (\d+))?$', $this, 'botRate' );
		$this->_setHandler( '^(max)?rate add', $this, 'botRateAdd' );
		$this->_setHandler( '^(max)?rate list', $this, 'botRateList' );
		$this->_setHandler( '^(max)?rate del(ete)?', $this, 'botRateDel' );
		
		$this->_setHandler( '^clear', $this, 'botClear' );
		$this->_setHandler( '^cancel', $this, 'botCancel' );
		$this->_setHandler( '^del(ete)?', $this, 'botDel' );
		
		$this->_setHandler( '^help', $this, 'botHelp' );
		$this->_setHandler( '^commands', $this, 'botCommands' );
		
		// connect to xmlrpc server
		$this->_connect();
		
		if ( !$irc->isWorker ) {
			$irc->_modules['store']->addVariable( '_hellaRate' );
			$irc->_hellaSSTimer = $irc->registerTimehandler( 2000, $this, '_setupSetupTimers' );
		}
	}
	
	function _customClose()
	{
		global $irc;
		
		$this->_unsetRateTimers();
		$irc->_modules['store']->manualUpdate( '_hellaRate', true );
	}

	/*****************************************************
	 * Bot functions
	 *****************************************************/	
	
	function botStatus( &$irc, &$data, $notice )
	{
		if ( ( $hella = $this->_send( 'status' ) ) !== false )
		{					
			$vars = array(
				'version' => $hella['version'],
				'uptime' => $hella['uptime'],
				'total_dl_nzbs' => number_format( $hella['total_dl_nzbs'] ),
				'total_dl_files' => number_format( $hella['total_dl_files'] ),
				'total_dl_mb' => $irc->_modules['func']->humanSize( $hella['total_dl_mb'], 2 ),
				'queued_mb' => $irc->_modules['func']->humanSize( $hella['queued_mb'], 2 ),
				'eta' => $irc->_modules['func']->duration( $hella['eta'], true ),
				'percent_complete' => $hella['percent_complete'] );
				
			if ( count( $hella['queued'] > 0 ) )
			{
				$qmb = $this->getQueueSize( $hella['queued'] );
				if ( $hella['rate'] > 0 )
				{
					$qtime = $qmb * 1024 / ( $hella['rate'] + 0.0001 );
					$stime = ', '.substr( $irc->_modules['func']->duration( $qtime ), 0, -1 );
				}
				else
				{
					$stime = '';
				}

				$vars['queuedItems'][] = array(
					'total' => count( $hella['queued'] ),
					'size' => $irc->_modules['func']->humanSize( $qmb, 2 ),
					'time' => $stime
					);
			}
			else
			{
				$vars['queuedItems'] = 'None';
			}
		
			// status
			$vars['status'] = ( $hella['is_paused'] == 1 )? 'Paused':
							  ( ( $hella['rate'] == 0 )? 'Idle':
			                  round( $hella['rate'], 1 ).( ( $hella['maxrate'] > 0 )? '/'.$hella['maxrate']:'').' **kb/s**' );
				
			if ( count( $hella['currently_downloading'] ) == 0 )
				$vars['downloading'] = 'None';
			else
			{
			 	for( $i=0; $i < count( $hella['currently_downloading'] ); $i++ )
			 	{
					$hella['currently_downloading'][$i]['nzbName'] = trim( str_replace( '_', ' ', $hella['currently_downloading'][$i]['nzbName'] ) );
				}
				$vars['downloading'] = $hella['currently_downloading'];
			}
			
			if ( count( $hella['currently_processing'] ) == 0 )
				$vars['processing'] = 'None';
			else
			{
			 	for( $i=0; $i < count( $hella['currently_processing'] ); $i++ )
			 	{
					$hella['currently_processing'][$i]['nzbName'] = trim( str_replace( '_', ' ', $hella['currently_processing'][$i]['nzbName'] ) );
				}			 
				$vars['processing'] = $hella['currently_processing'];
			}
			
			$this->parseTemplate( $msg, 'status', $vars );
			
			if ( isset( $msg ) )
				$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
			
		}
		else
		{
			$msg[] = $this->_error;
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}
	
	function botList( &$irc, &$data, $notice )
	{
		if ( ( $list = $this->listSearch( $data->subMessage ) ) !== false )
		{
		 	if ( count( $list ) == 0 )
		 	{
				$this->parseTemplate( $msg, 'listEmpty', array( 'search' => $data->subMessage ) );
			}
			else if ( count( $list ) > $this->_config['list']['compactLimit'] )
			{
				$tmp['listItemShort'] = $list;
				$this->parseTemplate( $msg, 'listShort', $tmp );
			}
			else
			{
			 	foreach( $list as $item )
			 	{
					$this->parseTemplate( $msg, 'listItem', $item );
				}
			}
			
			if ( isset( $msg ) )
			{
				$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
			}
		}
		else
		{
			$msg[] = $this->_error;
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}
	
	function botPause( &$irc, &$data, $notice )
	{
		if ( $this->_send( 'pause' ) !== false )
		{
			if ( strlen( $data->subMessage ) > 1 )
			{
			 	$num = array( 'w' => 604800, 'd' => 86400, 'h' => 3600, 'm' => 60, 's' => 1 );
				if ( preg_match_all( $this->_def['regex']['itemAge'], $data->subMessage, $matches ) )
				{
					for( $i=0; $i < count($matches[0]); $i++ ) {
						$time += $matches[1][$i] * $num[$matches[2][$i]];
					}
				}
			}
			if ( $time < 60 )
			{
				$time = 3600;
			}
			if ( isset( $irc->_hellaPauseID ) )
			{
				$irc->unregisterTimeid( $irc->_hellaPauseID );
			}
			$irc->_hellaPauseID = $irc->registerTimehandler( ($time) * 1000, $this, 'unpause' );
			$vars['pauseLength'] = $irc->_modules['func']->duration( $time );
			
			$this->parseTemplate( $msg, 'paused', $vars );
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );			
		}
	}
	
	function botUnPause( &$irc, &$data, $notice )
	{
		if ( $this->_send( 'continue' ) !== false )
		{
			if ( isset( $irc->_hellaPauseID ) )
			{
				$irc->unregisterTimeid( $irc->_hellaPauseID );
				unset( $irc->_hellaPauseID );
			}
			
			$this->parseTemplate( $msg, 'unpaused', array() );
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}	

	function botMove( &$irc, &$data, $notice )
	{
		switch ( $data->trig[1] )
		{
			case 'force':
			case 'next':
			case 'last':
			case 'front':
			case 'end':
			{
				$smEx = explode( ' ', $data->subMessage );
				if ( isset( $smEx[0] ) && preg_match( '/^(\d+)$/', $smEx[0] ) )
				{
					foreach( $smEx as $sNum )
					{
				 		$searchList[] = $sNum;
					}
				}
				else
				{
					$searchList[] = $data->subMessage;
				}
				
				foreach( $searchList as $search )
				{	
				 	// check to see if it exists
				 	if ( ( $list = $this->listSearch( $search ) ) !== false )
					{
						if ( count( $list ) == 1 )
						{
							$ta = array( 'last' => -2, 'next' => -1, 'force' => 0, 'end' => -2, 'front' => -1 );
							$this->moveItem( $list[0]['msgid'], $ta[$data->trig[1]], $irc->_modules['func']->getTData( $data ) );
						}
						else if ( count( $list ) == 0 )
						{
							$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'No Results found in the queue for query: {query}', 'query' => $search ) );
						}
						else
						{
							$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'Too many Results found in the queue for query: {query}', 'query' => $search ) );
						}
					}
					else
					{
						$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
					}
				}
				break;
			}
			case 'move':
			{
				if ( preg_match( $this->_def['regex']['moveID'], $data->subMessage, $match ) )
				{
					$smEx = explode( ' ', $match[2] );
					if ( isset( $smEx[0] ) && preg_match( '/^(\d+)$/', $smEx[0] ) )
					{
						foreach( $smEx as $sNum )
						{
					 		$searchList[] = $sNum;
						}
					}
					else
					{
						$searchList[] = $match[2];
					}
					
					foreach( $searchList as $search )
					{	
					 	// check to see if it exists
					 	if ( ( $list = $this->listSearch( $search ) ) !== false )
						{
							if ( count( $list ) == 1 )
							{
								$this->moveItem( $list[0]['msgid'], $match[2], $irc->_modules['func']->getTData( $data ) );
							}
							else if ( count( $list ) == 0 )
							{
								$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'No Results found in the queue for query: {query}', 'query' => $search ) );
							}
							else
							{
								$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'Too many Results found in the queue for query: {query}', 'query' => $search ) );
							}
						}
						else
						{
							$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
						}
					}					
				}
				else
				{
					$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'Query: {query} did not match the correct format', 'query' => $data->subMessage ) );
				}
				break;
			}
		}
		if ( isset( $msg ) )
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
	}
	
	function botRate( &$irc, &$data, $notice )
	{
		if ( strlen( $data->trig[2] ) > 0 )
		{
		 	if ( is_numeric( $data->trig[2] ) )
		 	{
				if ( ( $hella = $this->_send( 'maxrate', array( $data->trig[2] ) ) ) !== false )
				{
					$this->parseTemplate( $msg, 'newrate', array( 'maxrate' => $data->trig[2] ) );
				}
				else
				{
					$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
				}
			}
			else
			{
				$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'New rate: %s, is not a number', $data->subMessage ) ) );
			}
		}
		else
		{
			if ( ( $hella = $this->_send( 'maxrate' ) ) !== false )
			{
				$this->parseTemplate( $msg, 'currentrate', array( 'maxrate' => $hella ) );
			}
			else
			{
				$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
			}			
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}
	
	
	function botRateAdd( &$irc, &$data, $notice )
	{
		if ( preg_match( $this->_def['regex']['rateAdd'], $data->subMessage, $match ) )
		{
			// check the time works
			$time = strtotime( '1970/01/01 '.$match[2] );
			
			if ( $time > -1 )
			{
				$tFor = date( 'h:ia', $time );
				$irc->_hellaRate[] = array(
					'time' => $tFor,
					'rate' => $match[1],
					'data' => $irc->_modules['func']->getTData( $data ) );
				$this->_unsetRateTimers();
				$this->_setupRateTimers();
				$this->parseTemplate( $msg, 'rateAdd', array( 'time' => $tFor, 'rate' => $match[1] ) );
			}
			else
			{
				$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'Time is in an invalid format, could not understand: {time}', 'time' => $match[2] ) );
			} 
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}
	
	function botRateList( &$irc, &$data, $notice )
	{
		if ( isset( $irc->_hellaRate ) ) 
		{
			foreach( $irc->_hellaRate as $id => $rate )
			{
				if ( $rate['rate'] == 0 )
					$rate['rate'] = 'Unlimited';
				$vars['rateListTime'][] = $rate;
			}
			$this->parseTemplate( $msg, 'rateList', $vars ); 
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}		
	}
	
	function botRateDel( &$irc, &$data, $notice )
	{
		if ( isset( $irc->_hellaRate ) )
		{
			$cTime = date( 'h:ia', strtotime( '1970/01/01 '.$data->subMessage ) );
			foreach( $irc->_hellaRate as $id => $rate )
			{
				if ( $rate['time'] == $cTime )
				{				
					$this->_unsetRateTimers();
					unset( $irc->_hellaRate[$id] );					
					$this->_setupRateTimers();
					$this->parseTemplate( $msg, 'rateDel', array( 'time' => $cTime ) );
					$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
					return;
				}
			}
			$this->parseTemplate( $msg, 'error', array( 'errormsg' => 'No rate schedule entry matches time: {time}', 'time' => $cTime ) );
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}
	
	
	function botDel( &$irc, &$data, $notice )
	{
		if ( strlen( $data->subMessage ) > 0 )
		{
			$sMex = explode( ' ', $data->subMessage );
			if ( is_numeric( $sMex[0] ) )
			{
				foreach( $sMex as $item )
				{
					$this->del( $msg, $item );
				}
			}
			else
			{
				$this->del( $msg, $data->subMessage );
			}
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
		}
	}
	
	function botHelp( &$irc, &$data, $notice )
	{
		if ( strlen( $data->subMessage ) > 0 )
		{
			if ( isset( $this->_help[$data->subMessage] ) )
			{
				$this->parseHelp( $msg, $data->subMessage, array( 'trigger' => $data->trigger ) );
			}
			else
			{
				//$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'Unknown command: %s', $data->subMessage ) ) );
			}
		}
		else
		{
			foreach ( $this->_help as $id => $helpItem )
			{
				$vars['helpCommands'][] = $id;
			}
			$vars['trigger'] = $data->trigger;
			$vars['subTrigger'] = $data->trig[0];
			$this->parseTemplate( $msg, 'helpList', $vars );
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, true );
		}
		
	}
	
	function botCommands( &$irc, &$data, $notice )
	{
		$vars['trigger'] = $data->trigger;
		
		$this->parseTemplate( $msg, 'commandList', $vars );
		
		foreach( $this->_help as $id => $helpItem )
		{
			if ( is_array( $helpItem ) )
			{
				$helpItem['trigger'] = $data->trigger;
				$this->parseTemplate( $msg, 'help', $helpItem );			
			}
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, true );
		}
	}
	
	function botCancel( &$irc, &$data, $notice )
	{
		if ( ( $hella = $this->_send( 'cancel' ) ) !== false )
		{
			$this->parseTemplate( $msg, 'cancel', false );
		}
		else
		{
			$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
		}

		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, true );
		}		
	}
	
	function botClear( &$irc, &$data, $notice )
	{
		if ( ( $hella = $this->_send( 'clear' ) ) !== false )
		{
			$this->parseTemplate( $msg, 'clear', false );
		}
		else
		{
			$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
		}		

		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $data, $msg, true );
		}		
	}

	/*****************************************************
	 * Bot Internal Functions
	 *****************************************************/

	function del( &$msg, $query )
	{
		if ( ( $list = $this->listSearch( $query ) ) !== false )
		{
			if ( count( $list ) == 0 )
			{
				$this->parseTemplate( $msg, 'listEmpty', array( 'search' => $query ) );
				return false;
			}
			else if ( count( $list ) ==  1 )
			{
				if ( $this->_send( 'dequeue', array( $list[0]['id'] ) ) )
				{
					$this->_template['listItem'] = '({queuePos}) [{id}:{msgid}] {nzbName}';
					$this->parseTemplate( $msg, 'del', array( 'listItem' => $list ) );
				}
				return true;
			}
			else
			{
				$this->_template['listItem'] = '({queuePos}) [{id}:{msgid}] {nzbName}';
				$tmp['listItem'] = $list;
				$tmp['search'] = $query;
				$this->_template['listShort'] = 'Too many matches found for: {search}: {listItem}';
				$this->parseTemplate( $msg, 'listShort', $tmp );
				return false;
			}
		}
	}
	
	function unpause( &$irc )
	{
		if ( $this->_send( 'continue' ) !== false )
		{
			$irc->unregisterTimeid( $irc->_hellaPauseID );
			unset( $irc->_hellaPauseID );
		}
	}
	 
	/**
	 * Search through the list
	 *
	 * @var string $search - Search phrase
	 * @return list of items
	 * @access public
	 */
	function listSearch( $search )
	{
		global $irc;
		
		$vars = array();
		
	 	$num = array( 'w' => 604800, 'd' => 86400, 'h' => 3600, 'm' => 60, 's' => 1 );		
		
		if ( strlen( $search ) > 1 )
		{
			foreach( $this->_def['regex']['listFlags'] as $name => $regex )
			{
			 	if ( is_array( $regex ) )
			 	{
					foreach( $regex as $reg )
					{
						if ( preg_match( $reg, $search, $match ) )
						{
							$flag[$name] = $match;
							$search = str_replace( $match[0], '', $search );
							break;
						}
					}
				}
				else
				{
					if ( preg_match( $regex, $search, $match ) )
					{
						$flag[$name] = $match;
						$search = str_replace( $match[0], '', $search );
					}					
				}
			}
		}
		
		if ( ( $hella = $this->_send( 'status' ) ) !== false )
		{
		 	$limit = ( isset( $flag['limit'] ) )? $flag['limit'][1]:$this->_config['list']['positionRange'];
		 	if ( isset( $flag['top'] ) )
		 	{
				$start = 0;
			}
			else if ( isset( $flag['end'] ) )
			{
				$start = count( $hella['queued'] ) - $this->_config['list']['positionRange'];
				$start = ( $start > 0 )? $start:0;
			}
			else if ( isset( $flag['pos'] ) )
			{
				$start = $flag['pos'][1] - 1 - floor( $this->_config['list']['positionRange'] / 2 );
				if ( $start + $this->_config['list']['positionRange'] > count( $hella['queued'] ) )
					$start = count( $hella['queued'] ) - $this->_config['list']['positionRange'];
				$start = ( $start > 0 )? $start:0;
			}
			else
			{
				$start = 0;
				$limit = ( isset( $flag['limit'] ) )? $flag['limit'][1]:9999;
			}
		 	
		 	for( $i = $start; $i < count( $hella['queued'] ); $i++ )
		 	{
		 	 	if ( $i - $start < $limit )
		 	 	{
		 	 	 	$succ = true;
		 	 	 	
		 	 	 	$item = $hella['queued'][$i];
			 	 	$item['queuePos'] = $i+1;
			 	 	$item['nzbName'] = trim( str_replace( '_', ' ', $item['nzbName'] ) );
			 	 	if ( preg_match( '/^\([^\)]+\) (.+)/i', $item['nzbName'], $m ) )
			 	 	{
						$item['nzbName'] = $m[1];
					}
			 	 	
					if ( $item['msgid'] > 0 )
					{
						if ( isset( $irc->_nzbHistory[$item['msgid']] ) )
						{
							$iHistory = $irc->_nzbHistory[$item['msgid']];
						}
					}
								 	 	
			 	 	// check for user
			 	 	if ( ( isset( $flag['user'] ) ) && ( isset( $iHistory ) ) )
			 	 	{
						if ( $flag['user'][1] != $iHistory['data']->nick )
						{
							$succ = false;
						}
					}
					
	 	 			if ( isset( $iHistory ) )
					{
						if ( $item['msgid'] > 0 )
						{
							$iHistory['report'] = $irc->_modules['nzb']->getReport( $item['msgid'] );
							$age = $irc->_modules['func']->duration( time() - $iHistory['report']['report:reported'] );
							$item['listItemInfo'][] = array(
								'user' => $iHistory['data']->nick.', ',
								'category' => ', '.$iHistory['report']['report:category'],
								'queued' => $irc->_modules['func']->nicedate( $iHistory['queued'] ).', ',
								'size' => $irc->_modules['func']->humanSize( $iHistory['report']['report:size']['_content'] ),
								'age' => ', '.trim( $age )
								);
						}
					}
					else if ( $item['msgid'] > 0 )
					{ 
						if ( ( $iHistory = $irc->_modules['nzb']->getReport( $item['msgid'] ) ) !== false )
						{
							$age = $irc->_modules['func']->duration( time() - $iHistory['report:reported'] );
							$item['listItemInfo'][] = array(
								'category' => ', '.$iHistory['report:category'],
								'size' => $irc->_modules['func']->humanSize( $iHistory['report:size']['_content'] ),
								'age' => ', '.trim( $age )
								);
						}
					}
					else
					{
						$item['listItemInfo'][] = array(
							'size' => $item['total_mb'] );
					}
			 	 	
			 	 	if ( strlen( $search ) > 1 )
			 	 	{
			 	 	 	if ( is_numeric( $search ) )
			 	 	 	{
							if ( $search == $item['id'] || $search == $item['msgid'] )
							{
								$vars = array( $item );
								return $vars;
							}
						}
			            if (substr($search, 0, 1) == '/') {
			                $regex = '.*'.str_replace(' ', '.*', $search ).'.*';
			            } else {
			                $regex = '/.*'.str_replace(' ', '.*', $search ).'.*/i';
			            }		 	 	 
						if ( !preg_match( $regex, $item['nzbName'] ) )
						{
							$succ = false;
						}
					}

					if ( $succ )
					{
						$vars[] = $item;
					}
					
					unset( $succ, $item, $iHistory, $age, $time, $matches );
				}
			}
			return $vars;
		}
		else
		{
			return false;
		}
		
	}
	
	function getQueueSize( $queue = false )
	{
	 	global $irc;
		
		$tsize = 0;
		
		if ( $queue === false )
		{
			if ( ( $hella = $this->_send( 'status' ) ) !== false )
			{
			 	$queue = $hella['queued'];
			}
		}	
		foreach( $queue as $item )
		{
			if ( isset( $item['total_mb'] ) )
			{
				$tsize += str_replace( ',', '', $item['total_mb'] );
			}
			else
			{
				$rep = $irc->_modules['nzb']->getReport( $item['msgid'] );
				$tsize += ( $rep['report:size']['_content'] / (1024*1024) ); // convert from byte to mb
			}
		} 
		
		return $tsize;
	}
	
	/*****************************************************
	 * Queue moving malarky
	 *****************************************************/
	 
	function moveItem( $id, $pos, $tdata )
	{
	 	global $irc;
	 	
	 	$irc->modDebug( 'hella', 'Attempting to move item: '.$id.' position: '.$pos, __FILE__, __LINE__ );
	 	
		// cant find the item, start a timer (for 30 secs to check if it is in the list)
		$item = array(
			'timerID' => $irc->registerTimeHandler( 30000, $this, 'moveTimer' ),
			'id' => $id,
			'pos' => $pos,
			'data' => $tdata,
			'time' => time()
			);
		$irc->_hellaMoveID[$id] = $item;
		$this->moveTimer( $irc );
	}
	
	function moveTimer( &$irc )
	{
	 	$mList = array(	'last', 'next', 'force' );
		foreach( $irc->_hellaMoveID as $id => $item )
		{
			$list = $this->listSearch( $id );
			if ( count( $list ) == 1 )
			{
				$litem = $list[0];
				// if pos = 0 force, -1 next, -2 last
				if ( $item['pos'] <= 0 )
				{
					if ( $this->_send( $mList[$item['pos']+2], array( $litem['id'] ) ) !== false )
					{
					 	unset( $irc->_hellaMoveID[$id] );
					 	$irc->unregisterTimeid( $item['timerID'] );	
					 	
					 	$irc->modDebug( 'hella', 'Moved item: '.$id.' to position: '.$mList[$item['pos']+2], __FILE__, __LINE__ );
					 	
					 	if ( $irc->isWorker == false )
						{			 	
						 	$vars['downloading'][] = array(
						 		'id' => $litem['id'],
						 		'msgid' => $litem['msgid'],
						 		'nzbName' => $litem['nzbName'] );
						 	$this->parseTemplate( $msg, $mList[$item['pos']+2], $vars ); 
							$irc->_modules['func']->reply( $irc, $item['data'], $msg, $item['data']->notice );
						}
					}
					else
					{
						$irc->modDebug( 'hella', $this->_error, __FILE__, __LINE__ );
					}
				}
				else
				{
					if ( $this->_send( 'move', array( $item['id'], $item['pos'] ) ) !== false )
					{
					 	unset( $irc->_hellaMoveID[$id] );
					 	$irc->unregisterTimeid( $item['timerID'] );
					 	
					 	$irc->modDebug( 'hella', 'Moved item: '.$id.' to position: '.$item['pos'].' in queue', __FILE__, __LINE__ );
					 	
					 	if ( $irc->isWorker == false )
						{
						 	$vars['downloading'][] = array(
						 		'id' => $litem['id'],
						 		'msgid' => $litem['msgid'],
						 		'nzbName' => $litem['nzbName'] );
						 	$vars['position'] = $item['pos'];
						 	
						 	$this->parseTemplate( $msg, 'move', $vars ); 
							$irc->_modules['func']->reply( $irc, $item['data'], $msg, $item['data']->notice );
						}
					}
				}
			}
			if ( $item['time'] + $this->_config['moveTimerMax'] * 3600 < time() )
			{
			 	$irc->unregisterTimeid( $item['timerID'] );
				unset( $irc->_hellaMoveID[$id] );
			}
		}
	}
	
	/*****************************************************
	 * Hellanzb Communcation functions
	 *****************************************************/	
	
	function _connect()
	{
	 	global $irc;
	 	
		$irc->_hellaCli = new XML_RPC_Client( '/', $this->_config['xmlrpc']['hostname'], $this->_config['xmlrpc']['port'] );
		
		$irc->_hellaCli->setCredentials( 'hellanzb', $this->_config['xmlrpc']['password'] );
		
		$irc->modDebug( 'hella', sprintf( 'Connected to XML-RPC on '.$this->_config['xmlrpc']['hostname'] ), __FILE__, __LINE__ );
	}
	
	/**
	 * Send a Message
	 *
	 * @var string $msg   - Message to send
	 * @var array $params - Values to send
	 * @var bool $xml     - the $params var is allready set as XML_RPC values
	 * @return response
	 * @access private
	 */
	function _send( $msg, $params = false, $xml = false )
	{
	 	global $irc;
	 
	 	if ( $params !== false )
	 	{
		 	if ( !$xml )
		 	{
		 	 	foreach( $params as $prm )
		 	 	{
					$opm[] = XML_RPC_encode( $prm );
				}
			}
			else
			{
				$opm = $params;
			}			
			$xmlmsg = new XML_RPC_Message( $msg, $opm );			
		}
		else
		{
			$xmlmsg = new XML_RPC_Message( $msg );
		}

		$resp = $irc->_hellaCli->send( $xmlmsg );
		
		// check communication error
		if ( !$resp )
		{
		 	$this->_error = sprintf( 'XML-RPC Communcation Error: [%d] %s', $irc->_hellaCli->errno, $irc->_hellaCli->errstr );
			$irc->modDebug( 'hella', $this->_error, __FILE__, __LINE__ );
			return false;
		}
		
		if ( $resp->faultCode() )
		{
		 	$xmlmsg->createPayload();
		 	$this->_error = sprintf( 'Problem with data (%s) sent: [%d] %s', $xmlmsg->payload, $resp->faultCode(), $resp->faultString() );
			$irc->modDebug( 'hella', $this->_error, __FILE__, __LINE__ );
			return false;
		}
		else
		{			
			$val = $resp->value();
			
			/*
			$tmp = var_export( $val, true );
			$fp = fopen( 'hellatmp.txt', 'w' );
			fwrite( $fp, $tmp );
			fclose( $fp );
			*/
			
			return XML_RPC_decode( $val );
		}
		
	}
	
	/*****************************************************
	 * Hellanzb Communcation functions
	 *****************************************************/		
	
	function _setupSetupTimers( &$irc )
	{
		$irc->unregisterTimeid( $irc->_hellaSSTimer );
		unset( $irc->_hellaSSTimer );
		
		$irc->_modules['store']->manualUpdate( '_hellaRate' );
		if ( isset( $irc->_hellaRate ) )
		{
			foreach( $irc->_hellaRate as $id => $rate )
			{
				unset( $irc->_hellaRate[$id]['timer'], $irc->_hellaRate[$id]['initTimer'] );				
			}
		}		
		$this->_setupRateTimers();
	}
	
	function _setupRateTimers()
	{
		global $irc;
		
		$irc->_modules['store']->manualUpdate( '_hellaRate' );
		
		if ( isset( $irc->_hellaRate ) )
		{
			foreach( $irc->_hellaRate as $id => $rate )
			{
				$nTime = strtotime( date('Y/m/d ').$rate['time'] );
				$nTime = $nTime - time();
				if ( $nTime < 5 )
					$nTime += 86400;
				
				$irc->_hellaRate[$id]['initTimer'] = $irc->registerTimeHandler( $nTime * 1000, $this, '_hellaInitialRate', array( 'id' => $id ) );
				$irc->modDebug( 'hella', 'Registered initial rate time handler: '.$rate['time'].', in '.$irc->_modules['func']->duration($nTime).' rate: '.$rate['rate'], __FILE__, __LINE__ );
			}
			$irc->_modules['store']->manualUpdate( '_hellaRate', true );
		}
	}
	
	function _unsetRateTimers()
	{
		global $irc;
		
		$irc->_modules['store']->manualUpdate( '_hellaRate' );
		
		if ( isset( $irc->_hellaRate ) )
		{
			foreach( $irc->_hellaRate as $id => $rate )
			{
				if ( isset( $rate['initTimer'] ) )
				{
					$irc->unregisterTimeid( $rate['initTimer'] );
					$irc->modDebug( 'hella', 'Unregistered initial time handler for: '.$rate['time'].', rate: '.$rate['rate'], __FILE__, __LINE__ );
					unset( $irc->_hellaRate[$id]['initTimer'] );
				}
				if ( isset( $rate['timer'] ) )
				{
					$irc->unregisterTimeid( $rate['timer'] );
					$irc->modDebug( 'hella', 'Unregistered regular time handler for: '.$rate['time'].', rate: '.$rate['rate'], __FILE__, __LINE__ );
					unset( $irc->_hellaRate[$id]['timer'] );
				}
			}
			$irc->_modules['store']->manualUpdate( '_hellaRate', true );
		}
	}
	
	function _hellaInitialRate( &$irc, $vars )
	{
		$rID = $vars['id'];
		
		$irc->_modules['store']->manualUpdate( '_hellaRate' );

		$irc->unregisterTimeid( $irc->_hellaRate[$rID]['initTimer'] );
		unset( $irc->_hellaRate[$rID]['initTimer'] );
		
		$irc->_hellaRate[$rID]['timer'] = $irc->registerTimeHandler( 86400000, $this, '_hellaRateTrigger', $vars ); // 1 day in ms
		$irc->modDebug( 'hella', 'Registered regular rate schedule for time : '.$irc->_hellaRate[$rID]['time'].', rate: '.$irc->_hellaRate[$rID]['rate'], __FILE__, __LINE__ );
		$irc->_modules['store']->manualUpdate( '_hellaRate', true );
		$this->_hellaRateTrigger( $irc, $vars );
	}
	
	function _hellaRateTrigger( &$irc, $vars )
	{
		$rID = $vars['id'];
		if ( $this->_send( 'maxrate', array( $irc->_hellaRate[$rID]['rate'] ) ) !== false )
		{
			$irc->modDebug( 'hella', 'Rate scheduler triggered, set rate to: '.$irc->_hellaRate[$rID]['rate'].' kb/s at '.$irc->_hellaRate[$rID]['time'], __FILE__, __LINE__ );
			$this->parseTemplate( $msg, 'newrate', array( 'maxrate' => $irc->_hellaRate[$rID]['rate'] ) );
		}
		else
		{
			$this->parseTemplate( $msg, 'error', array( 'errormsg' => $this->_error ) );
		}
		
		if ( isset( $msg ) )
		{
			$irc->_modules['func']->reply( $irc, $irc->_hellaRate[$rID]['data'], $msg, $irc->_hellaRate[$rID]['data']->notice );
		}
	}
	
	/**
	 * Get the current entry in the startTime split
	 * 
	 * @param string $name - Variable name in startTime config array
	 * @param bool $posOnly - Only return the position
	 * @return array $tStart, $tStop - The starting and stopping positions of the autotvInfo array
	 */
	function _timerSplit( )
	{
		global $irc;
		
		$cMin = strtotime( '1970/01/01 '.date('H:i:s') );
			
		foreach( $irc->_hellaRate as $id => $rate )
		{
			$cTime = strtotime( '1970/01/01 '.$rate['time'] );
			if ( ( $cMin >= $cTime ) &&
			     ( $cMin < $cTime + 120 ) )
			{
				return $id;
			}
		}
		return false;
	}		
}	
Return current item: Nzbirc