All pastes #37868 Raw Edit

torrentspy 1.0

public text v1 · immutable
#37868 ·published 2006-01-21 19:18 UTC
rendered paste body
#---------------------------------------------------------------#
# redeemer:torrentspy                                      v1.0 #
#                                                               #
# performs TorrentSpy searches.                                 #
# tested on Eggdrop v1.6.17                                     #
#                                                               #
# Usage:                                                        #
#   .chanset #channel +torrentspy                               #
#   !torrentspy                                                 #
#                                                               #
# ChangeLog:                                                    #
#   1.0: first public release                                   #
#                                                               #
# BUGS:                                                         #
#   - If you find something quirky, my e-mail is at the bottom  #
#     of this header.                                           #
#                                                               #
# TODO:                                                         #
#   - nothing                                                   #
#                                                               #
# LICENSE:                                                      #
#   This code comes with ABSOLUTELY NO WARRANTY.                #
#                                                               #
#   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. (http://www.gnu.org/copyleft/library.txt)          #
#                                                               #
# Copyright (C) 2006, Redeemer                                  #
# redeemer@zion-mainframe.net                                   #
#---------------------------------------------------------------#
package require http 2.3
setudef flag torrentspy

# 0 will typically disable an option, otherwise a value 1 or
# above will enable it.
#
namespace eval redeemer {
  namespace eval torrentspy {
    # set this to the command character you wish to use
    variable command_char "@"

    # set these to the trigger(s) you wish to use ("trig1 trig2 trig3")
    variable tspy_binds "tspy torrent torrents torrentspy torspy"

    # To restrict usage to Opped (3), HOpped (2) or Voiced (1) or All (0) users
    # on all +torrentspy channels, change the variable below
    variable chan_user_level 0

    # you want to allow users to search via /msg, enable this
    variable private_messages 1

    # as per emailed & forum requests, use the next two variables together
    # to determine the output type like so:
    #  notice_reply 1 & force_private 1 = private notice reply only (this is as requested)
    #  notice_reply 0 & force_private 1 = private msg reply only
    #  notice_reply 1 & force_private 0 = regular channel OR private NOTICE
    #  notice_reply 0 & force_private 0 = regular channel OR private MSG (default)
    # set to 1 to enable a /notice reply instead, 0 for normal text
    variable notice_reply 0
    # set to 1 to force all replies to be private
    variable force_private 0

    # number of search results/image links to return, 'define:' is always 1 as some defs are huge
    variable search_results 3

    # what to use to seperate results, use "\n" for one result per line
    variable seperator "\n"

    # ** this is not an optional setting, if a string is too long to send, it won't be sent! **
    # It should be set to the max amount of characters that will be received in a public
    #   message by your IRC server.  If you find you aren't receiving results, try lowering this.
    variable split_length 443

    # Use these two settings to set colours, bold, reverse, underline etc on either descriptions or links
    # The following modes apply and you can use any combination of them: (NO SPACES!)
    #
    #  Bold = \002
    #  Underline = \037
    #  Reverse = \026
    #  Colors:                  #RGB/Html code:
    #   White = \00300          #FFFFFF
    #   Black = \00301          #000000
    #   Blue = \00302           #00007F
    #   Green = \00303          #008F00
    #   Light Red = \00304      #FF0000
    #   Brown = \00305          #7F0000
    #   Purple = \00306         #9F009F
    #   Orange = \00307         #FF7F00
    #   Yellow = \00308         #F0FF00
    #   Light Green = \00309    #00F700
    #   Cyan = \00310           #008F8F
    #   Light Cyan = \00311     #00F7FF
    #   Light Blue = \00312     #0000FF
    #   Pink = \00313           #FF00FF
    #   Grey = \00314           #7F7F7F
    #   Light Grey = \00315     #CFCFCF
    #
    # This example will do Bold, Underline and Light Blue: "\002\037\00312"
    # Note: This will affect *ALL* descs or links. Dont forget to use the \ too !
    # Also note, abusing this this heavily increases the number of characters per line,
    #  so your output lines will increase somewhat.
    variable desc_modes "\00307"
    variable link_modes "\037\00311"

    # number of minute(s) to ignore flooders, 0 to disable flood protection
    variable ignore 1

    # how many requests in how many seconds is considered flooding?
    # by default, this allows 3 queries in 10 seconds, the 4th being ignored
    #   and ignoring the flooder for 'variable ignore' minutes
    variable flood 4:10
  }
}
# end of configuration, script begins

namespace eval redeemer {
  namespace eval torrentspy {
    variable version "redeemer:torrentspy-1.0"
  }
}

# bind the public binds
bind pubm -|- "*" redeemer::torrentspy::public_message

# bind the private message binds, if wanted
if {$redeemer::torrentspy::private_messages >= 1} {
  bind msgm -|- "*" redeemer::torrentspy::private_message
}

namespace eval redeemer {
  namespace eval torrentspy {
    #TorrentSpy - perform a search via TorrentSpy
    #
    proc torrentspy {input} {
      # local variable initialization
      set results 0 ; set break 0 ; set output ""

      # search_results is set to zero, stop now
      if {$redeemer::torrentspy::search_results <= 0} {
        return
      }
      
      # fetch the html
      set html [fetch_html $input]

      # parse the html
      while {$results < $redeemer::torrentspy::search_results} {
        # the regexp grabs the data and stores it using variables while
        # the regsub removes unnecessary data for the next loop to be clean

        # regular search
        regexp -- {16"></a>.*?href="(.+?)".*?"(.+?)">.*?-<.*?nowrap>(.+?)<.*?nowrap>.*?nowrap>(.+?)<.*?nowrap>(.+?)<.*?icons/(.+?).gif} $html - link desc size seed peer health
        regsub -- {16"></a>.*?href="(.+?)".*?"(.+?)">.*?-<.*?nowrap>(.+?)<.*?nowrap>.*?nowrap>(.+?)<.*?nowrap>(.+?)<.*?icons/(.+?).gif} $html {} html

        if {[info exists desc] == 0 {
          if {$output == ""} {
            set output "Sorry, no results found."
          }
          return $output
        }

        # remove trailing spaces from desc and convert %20 to space
        set desc [string trim $desc]
        regsub -all "%20" $desc " " desc
        
        if {[info exists desc] == 1 && $redeemer::torrentspy::desc_modes != ""} { set desc "$redeemer::torrentspy::desc_modes[string trim $desc]" }
 
        # add the search result
        if {$link != ""} {
          if {[info exists link] == 1 && $redeemer::torrentspy::link_modes !=""} { set link "$redeemer::torrentspy:link_modes[string trim $link]" }
          regsub -all "(?:\x93|\x94|&quot;|\")" $link {} link
          append output "${desc}\017 \002|\002 ${link}\017${redeemer::torrentspy::seperator}"
        }

        # increment results and clear variables for next loop
        unset link ; unset desc ; unset seed ; unset size ; unset peer; unset health ; set break 0
        incr results
      }

      # make sure there is data to output
      if {[info exists output] == 0} {
        set reply "Sorry, no results found."
        return $reply
      }
      return $output
    }

    # FETCH_HTML - fetches html for torrentspy.com site
    proc fetch_html {input} {
      # TorrentSpy
      global tcl_platform
      set query "http://www.torrentspy.com/search.asp?query="

      # setup & search for the torrents
      set ua "TCL/[info tclversion] ([string totitle $tcl_platform(platform)]; $tcl_platform(os)/$tcl_platform(osVersion)) http/[package provide http]"
      set input [::http::formatQuery $input]
      set input [string trimleft $input "+"]
      set query "${query}${input}"
      set http [::http::config -useragent $ua]
      set http [::http::geturl $query]
      set html [::http::data $http]

      # generic pre-parsing
      regsub -all "\n" $html "" html
      regsub -all "(?:\x91|\x92|&#39;)" $html {'} html
      regsub -all "(?:\x93|\x94|&quot;)" $html {"} html
      regsub -all "&amp;" $html {\&} html
      regsub -all -nocase {<sup>(.+?)</sup>} $html {^\1} html
      regsub -all -nocase {<font.+?>} $html "" html
      regsub -all -nocase {</font>} $html "" html
      regsub -all -nocase {<span.+?>} $html "" html
      regsub -all -nocase {</span>} $html "" html
      regsub -all -nocase {<input.+?>} $html "" html
      regsub -all -nocase {(?:<i>|</i>)} $html "" html

      # clean up our response
      regsub -all {(?:\n|\t)} $html {} html

      if {[info exists html]} {
        return $html
      } else {
        return "Something failed while attempting to connect to torrentspy.com."
      }
    }

    # PUBLIC_MESSAGE
    # decides what to do with binds that get triggered
    #
    proc public_message {nick uhand hand chan input} {
      if {[lsearch -exact [channel info $chan] +torrentspy] != -1} {
        if {$redeemer::torrentspy::chan_user_level == 3} {
          if {[isop $nick $chan] == 0} {
            return
          }
        } elseif {$redeemer::torrentspy::chan_user_level == 2} {
          if {[ishalfop $nick $chan] == 0 && [isop $nick $chan] == 0} {
            return
          }
        } elseif {$redeemer::torrentspy::chan_user_level == 1} {
          if {[isvoice $nick $chan] == 0 && [ishalfop $nick $chan] == 0 && [isop $nick $chan] == 0} {
            return
          }
        }
        send_output "$input" "$chan" "$nick" "$uhand"
      }
    }

    # PRIVATE_MESSAGE
    # decides what to do with binds that get triggered
    #
    proc private_message {nick uhand hand input} {
      if {$redeemer::torrentspy::private_messages >= 1} {
        send_output $input $nick $nick $uhand
      }
    }

    # SEND_OUTPUT
    # no point having two copies of this in public/private_message
    #
    proc send_output {input where nick uhand} {
      if {[encoding system] != "identity" && [lsearch [encoding names] "ascii"]} {
        set command_char [encoding convertfrom ascii ${redeemer::torrentspy::command_char}]
        set input [encoding convertfrom ascii $input]
      } elseif {[encoding system] == "identity"} {
        set command_char [encoding convertfrom identity ${redeemer::torrentspy::command_char}]
        set input [encoding convertfrom identity $input]
      } else {
        set command_char ${redeemer::torrentspy::command_char}
      }

      #Specifically retrieve only ONE (ascii) character, then check that matches the command_char first
      set trigger_char [string index $input 0]
      if {[encoding system] == "identity"} {
        set trigger_char [encoding convertfrom identity $trigger_char]
      }

      #Sanity check 1 - If no match, stop right here. No need to match every (first word) of
      # every line of channel data against every bind if the command_char doesnt even match.
      if {$trigger_char != $command_char} {
        return
      }

      set trigger [string range [lindex $input 0] 1 end]
      #Sanity check 2 - Stop if theres nothing to search for (quiet)
      set search [string trim [string range $input [string wordend $input 1] end]]
      if {$search == ""} { return }

      if {$redeemer::torrentspy::force_private == 1} { set where $nick }

      # check for torrentspy binds
      foreach bind [split $redeemer::torrentspy::tspy_binds " "] {
        if {[string match -nocase $bind $trigger] == 1} {
          # flood protection check
          if {[flood $nick $uhand]} {
            return
          }
          # call torrentspy
          foreach line [redeemer::torrentspy::parse_output [torrentspy $search]] {
            put_output $where $line
          }
          break
        }
      }
    }

 # PUT_OUTPUT
    # actually sends the output to the server
    proc put_output {where line} {
      if {$redeemer::torrentspy::notice_reply == 1} {
        putserv "NOTICE $where :$line"
      } else {
        putserv "PRIVMSG $where :$line"
      }
    }


    # PARSE_OUTPUT
    # prepares output for sending to a channel/user, calls line_wrap
    #
    proc parse_output {input} {
      set parsed_output [set parsed_current {}]
      if {[string match "\n" $redeemer::torrentspy::seperator] == 1} {
        regsub {\n\s*$} $input "" input
        foreach newline [split $input "\n"] {
          foreach line [redeemer::torrentspy::line_wrap $newline] {
            lappend parsed_output $line
          }
        }
      } else {
        regsub "(?:${redeemer::torrentspy::seperator}|\\|)\\s*$" $input {} input
        foreach line [redeemer::torrentspy::line_wrap $input] {
          lappend parsed_output $line
        }
      }
      return $parsed_output
    }

    # LINE_WRAP
    # takes a long line in, and chops it before the specified length
    # http://forum.egghelp.org/viewtopic.php?t=6690
    #
    proc line_wrap {str {splitChr { }}} {
      set out [set cur {}]
      set i 0
      set len $redeemer::torrentspy::split_length
      #regsub -all "\002" $str "<ZQ" str
      #regsub -all "\037" $str "<ZX" str
      foreach word [split [set str][set str ""] $splitChr] {
        if {[incr i [string len $word]] > $len} {
          #regsub -all "<ZQ" $cur "\002" cur
          #regsub -all "<ZX" $cur "\037" cur
          lappend out [join $cur $splitChr]
          set cur [list $word]
          set i [string len $word]
        } else {
          lappend cur $word
        }
        incr i
      }
      #regsub -all "<ZQ" $cur "\002" cur
      #regsub -all "<ZX" $cur "\037" cur
      lappend out [join $cur $splitChr]
    }

    # FLOOD_INIT
    # modified from bseen
    #
    variable flood_data
    variable flood_array
    proc flood_init {} {
      if {$redeemer::torrentspy::ignore < 1} {
        return 0
      }
      if {![string match *:* $redeemer::torrentspy::flood]} {
        putlog "$redeemer::torrentspy::version: variable flood not set correctly."
        return 1
      }
      set redeemer::torrentspy::flood_data(flood_num) [lindex [split $redeemer::torrentspy::flood :] 0]
      set redeemer::torrentspy::flood_data(flood_time) [lindex [split $redeemer::torrentspy::flood :] 1]
      set i [expr $redeemer::torrentspy::flood_data(flood_num) - 1]
      while {$i >= 0} {
        set redeemer::torrentspy::flood_array($i) 0
        incr i -1
      }
    }
    ; flood_init

    # FLOOD
    # updates and returns a users flood status
    #
    proc flood {nick uhand} {
      if {$redeemer::torrentspy::ignore < 1} {
        return 0
      }
      if {$redeemer::torrentspy::flood_data(flood_num) == 0} {
        return 0
      }
      set i [expr ${redeemer::torrentspy::flood_data(flood_num)} - 1]
      while {$i >= 1} {
        set redeemer::torrentspy::flood_array($i) $redeemer::torrentspy::flood_array([expr $i - 1])
        incr i -1
      }
      set redeemer::torrentspy::flood_array(0) [unixtime]
      if {[expr [unixtime] - $redeemer::torrentspy::flood_array([expr ${redeemer::torrentspy::flood_data(flood_num)} - 1])] <= ${redeemer::torrentspy::flood_data(flood_time)}} {
        putlog "$incith::google::version: flood detected from ${nick}."
        newignore [join [maskhost *!*[string trimleft $uhand ~]]] $redeemer::torrentspy::version flooding $redeemer::torrentspy::ignore
        return 1
      } else {
        return 0
      }
    }
  }
}

putlog " - $redeemer::torrentspy::version loaded."

# EOF