#!/usr/bin/env ruby
# vim:encoding=UTF-8:
=begin
# gig.rb

Github IRC Gateway

=end

$LOAD_PATH << "lib"
$LOAD_PATH << "../lib"

$KCODE = "u" unless defined? ::Encoding

require "rubygems"
require "net/irc"
require "net/https"
require "logger"
require "pathname"
require "libxml"
require "ostruct"
require 'time'

class ServerLogIrcGateway < Net::IRC::Server::Session
	EVENTS = {
		'DownloadEvent' => '6',
		'GistEvent'     => '10',
		'WatchEvent'    => '15',
		'FollowEvent'   => '15',
		'CreateEvent'   => '13',
		'ForkEvent'     => '3',
		'PushEvent'     => '14',
	}

	def server_name
		"github"
	end

	def server_version
		"0.0.0"
	end

	def main_channel
		@opts.main_channel || "#github"
	end

	def initialize(*args)
		super
		@last_retrieved = Time.now
		@cert_store = OpenSSL::X509::Store.new
		@cert_store.set_default_paths
	end

	def on_disconnected
		@retrieve_thread.kill rescue nil
	end

	def on_user(m)
		super
		@real, *@opts = @real.split(/\s+/)
		@opts = OpenStruct.new @opts.inject({}) {|r, i|
			key, value = i.split("=", 2)
			r.update key => case value
				when nil                      then true
				when /\A\d+\z/                then value.to_i
				when /\A(?:\d+\.\d*|\.\d+)\z/ then value.to_f
				else                               value
			end
		}
		post @nick, JOIN, main_channel

		@retrieve_thread = Thread.start do
			loop do
				begin
					@log.info 'retrieveing feed...'
					uri = URI.parse("https://github.com/#{@real}.private.atom?token=#{@pass}")
					http = Net::HTTP.new(uri.host, uri.port)
					http.use_ssl = true
					http.verify_mode = OpenSSL::SSL::VERIFY_PEER
					http.cert_store = @cert_store
					req = Net::HTTP::Get.new(uri.request_uri)
					res = http.request(req)

					doc = LibXML::XML::Document.string(res.body, :base_uri => uri.to_s)
					ns  = %w|a:http://www.w3.org/2005/Atom|
					entries = []
					doc.find('/a:feed/a:entry', ns).each do |n|
						entries << {
							:datetime => Time.parse(n.find('string(a:published)', ns)),
							:id       => n.find('string(a:id)', ns),
							:title    => n.find('string(a:title)', ns),
							:author   => n.find('string(a:author/a:name)', ns),
							:link     => n.find('string(a:link/@href)', ns),
						}
					end

					entries.reverse_each do |entry|
						next if entry[:datetime] <= @last_retrieved
						type = entry[:id][%r|tag:github.com,2008:(.+?)/\d+|, 1]
						post entry[:author], PRIVMSG, main_channel,
							"\003#{EVENTS[type] || '5'}#{entry[:title]}\017 \00314#{entry[:link]}\017"
					end

					@last_retrieved = entries.first[:datetime]
					@log.info 'sleep'
					sleep 30
				rescue Exception => e
					@log.error e.inspect
					e.backtrace.each do |l|
						@log.error "\t#{l}"
					end
					sleep 10
				end
			end
		end
	end
end

if __FILE__ == $0
	require "optparse"

	opts = {
		:port  => 16705,
		:host  => "localhost",
		:log   => nil,
		:debug => false,
		:foreground => false,
	}

	OptionParser.new do |parser|
		parser.instance_eval do
			self.banner  = <<-EOB.gsub(/^\t+/, "")
				Usage: #{$0} [opts]

			EOB

			separator ""

			separator "Options:"
			on("-p", "--port [PORT=#{opts[:port]}]", "port number to listen") do |port|
				opts[:port] = port
			end

			on("-h", "--host [HOST=#{opts[:host]}]", "host name or IP address to listen") do |host|
				opts[:host] = host
			end

			on("-l", "--log LOG", "log file") do |log|
				opts[:log] = log
			end

			on("--debug", "Enable debug mode") do |debug|
				opts[:log]   = $stdout
				opts[:debug] = true
			end

			on("-f", "--foreground", "run foreground") do |foreground|
				opts[:log]        = $stdout
				opts[:foreground] = true
			end

			parse!(ARGV)
		end
	end

	opts[:logger] = Logger.new(opts[:log], "daily")
	opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO

	def daemonize(foreground=false)
		trap("SIGINT")  { exit! 0 }
		trap("SIGTERM") { exit! 0 }
		trap("SIGHUP")  { exit! 0 }
		return yield if $DEBUG || foreground
		Process.fork do
			Process.setsid
			Dir.chdir "/"
			File.open("/dev/null") {|f|
				STDIN.reopen  f
				STDOUT.reopen f
				STDERR.reopen f
			}
			yield
		end
		exit! 0
	end

	daemonize(opts[:debug] || opts[:foreground]) do
		Net::IRC::Server.new(opts[:host], opts[:port], ServerLogIrcGateway, opts).start
	end
end

 
