hackerthreads.rb 4.48 KB
Newer Older
1 2 3 4 5
#!/usr/bin/env ruby

# Plugin to announce new posts in the channel
class Hackerthreads

6 7
	require 'net/http' # HTTP requests
	require 'nokogiri' # XML/HTML parsing
8 9 10

	# This method is called when the plugin is first loaded
	def initialize( status, config, output, irc, timer )
11 12 13 14 15
		@status    = status
		@config    = config
		@output    = output
		@irc       = irc
		@timer     = timer
16

17
		# Post announce settings
18
		@channel   = "#hackerthreads"
19

20 21
		@rss_host  = "www.hackerthreads.org"
		@rss_path  = "/rss.php"
22

23 24
		@timeout   = 120
		@last      = ""
25

26 27 28 29 30 31 32 33
		# Pending post announce settings
		@conf_file      = 'hackerthreads.json'
		@conf_pending   = []
		@reports        = []
		@reported_empty = false
		load_config

		# Kick off threads
34
		if( @status.threads && @config.threads)
35 36
			@rss_thread		= Thread.new{ check_rss }
			@pen_thread		= Thread.new{ check_pending }
37 38 39
		end
	end

40 41
	# Method to be called when the plugin is unloaded
	def unload
42
		if( @status.threads && @config.threads)
43 44
			@rss_thread.exit
			@pen_thread.exit
45
		end
46 47 48
		return true
	end

49
	# Default method
Cool Fire's avatar
Cool Fire committed
50
	def main( _nick, _user, _host, _from, _msg, _arguments, _con )
51 52 53 54 55 56
		check_rss
	end
	private

	# Thread to check for new posts
	def check_rss
Cool Fire's avatar
Cool Fire committed
57
		loop do
58 59 60 61 62 63 64
			begin
				# Grab rss
				xml = Net::HTTP.get( @rss_host, @rss_path )
				xml = Nokogiri::XML( xml )

				# Parse out info
				title, link = "", ""
65 66 67 68 69 70
				title  = xml.xpath( '//item/title' ).first.text
				link   = xml.xpath( '//item/link' ).first.text
				author = xml.xpath( '//item/author' ).first.text
				if( author =~ /\((.+?)\)$/ )
					author = $1
				end
71 72 73 74

				# Check if this is a new post
				if( @last != link )
					@last = link
75 76 77 78 79 80 81 82

					# If the tinyurl plugin is loaded, use it
					if( @status.checkplugin( "tinyurl" ) )
						plugin = @status.getplugin( "tinyurl" )
						link = plugin.main( nil, nil, nil, nil, nil, link, false )
					end

					@irc.message( @channel, "New post by #{author} | #{title} | #{link}" )
83 84 85
				end
			rescue
				# Silently fail
Cool Fire's avatar
Cool Fire committed
86
				@output.debug( "Failure while retreiving rss feed.\n" )
87 88 89 90 91 92
			end

			# Wait for a bit before fetching again
			sleep( @timeout )
		end
	end
93 94 95

	# Check posts pending approval
	def check_pending
Cool Fire's avatar
Cool Fire committed
96
		loop do
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
			begin

			base_url = @conf_pending['phpbb']['url']

			# Get authenticated
			sid = ''
			uri = URI( "#{base_url}#{@conf_pending['phpbb']['ucp']}?mode=login" )
			req = Net::HTTP::Post.new( uri )
			req.set_form_data(
				'username' => @conf_pending['phpbb']['auth']['user'],
				'password' => @conf_pending['phpbb']['auth']['pass'],
				'redirect' => 'index.php',
				'login'    => 'Login'
			)

			res = Net::HTTP.start( uri.hostname, uri.port ) do |http|
  			http.request( req )
			end

			if( res.get_fields( 'set-cookie' ).last =~ /^phpbb3_kjrto_sid=([0-9a-f]{32});/ ) 
				sid = $1
			else
				raise Exception.new( "Could not get a phpbb session." )
			end

			# Retreive pending posts page
			uri = URI( "#{base_url}#{@conf_pending['phpbb']['mcp']}" )
			params = {
				:i    => 'main',
				:mode => 'front',
				:sid  => sid
			}
			uri.query = URI.encode_www_form( params )

			res = Net::HTTP.get_response( uri )

			# Parse reponse
			html_doc = Nokogiri::HTML( res.body )
			if( html_doc.css('form#mcp_queue p').to_s =~ /no posts waiting for approval/ )
				if( not @reported_empty )
					@reports = []
					report_pending( 'No pending posts in queue.' )
					@reported_empty = true
				end
			else
				html_doc.css('form#mcp_queue ul.topiclist li.row').each do |e|
					report_pending( "Pending post \"#{e.css('a')[0].inner_html}\" by \"#{e.css('a')[1].inner_html}\" in \"#{e.css('a')[4].inner_html}\"" )
				end
				@reported_empty = false
			end

			
			rescue Exception => e
				# Silently fail
				@output.debug( "Failure while retreiving pending posts.\n" )
				@output.debug_extra( "#{e.inspect}\n" )
			end

			# Wait for a bit before fetching again
			sleep( @conf_pending['fetch']['repeat'] )
		end
	end

	# Reporting function for pending messages
	def report_pending( message )
		# Check if new
		if( !@reports.include? message )
			@reports.push( message )

			# Notify
			@conf_pending['report'].each do |r|
				case r['type']
				when "notice"
					@irc.notice( r['to'], message )
				when "message"
					@irc.message( r['to'], message )
				end
			end
		end
	end

	# Load config for pending post checks
	def load_config
Cool Fire's avatar
Cool Fire committed
180
		if File.exist?( @config.datadir + '/' + @conf_file )
181 182 183 184 185 186 187 188 189 190 191

			jsonline = ""
			File.open( @config.datadir + '/' + @conf_file ) do |file|
				file.each do |line|
					jsonline << line
				end
			end

			@conf_pending = JSON.parse( jsonline )
		end
	end
192
end