Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save FiXato/661632 to your computer and use it in GitHub Desktop.
Save FiXato/661632 to your computer and use it in GitHub Desktop.
Simple IMAP parser script that looks for NZBMatrix Watchlist mails and converts them to an RSS feed and downloads the NZBs.
#!/usr/bin/env ruby
# Simple IMAP parser script that looks for NZBMatrix Watchlist mails and converts them to an RSS feed and downloads the NZBs.
# Created by request of third party.
#
# LICENSE:
# Copyright 2010 Filip H.F. 'FiXato' Slagter
# You may use this work without restrictions, as long as this notice is included.
# The work is provided "as is" without warranty of any kind, neither express nor implied.
require 'net/imap'
require 'rss/maker'
require 'fileutils'
require 'yaml'
require 'set'
# DEBUG = (ARGV.include?('--debug') ? true : false)
DEBUG = true
STATUS_LOCATION = File.expand_path("~/.nzbmatrix_watchlist_mail2rss_status.yaml")
def debug(args,&block)
puts(args,&block) if DEBUG
end
class Config
CONFIG_LOCATION = File.expand_path("~/.nzbmatrix_watchlist_mail2rss_config.yaml")
class << self
def load
prompt unless exists?
@@config = YAML.load_file(CONFIG_LOCATION)
end
def exists?
File.exist?(CONFIG_LOCATION)
end
def config
@@config
end
def defaults
{
:imap => {
:server => 'imap.yourmailserver.example',
:user => '[email protected]',
:password => 'y0urp4ssw0rd!',
:folder => 'INBOX',
},
:mailparser_sender => '[email protected]',
:nzb_watchfolder => '~/downloads/nzb',
:nzbmatrix_api => {
:username => 'yourNzbMatrixUsername',
:key => 'yourNzbMatrixAPIKEY',
},
:rss => {
:version => '2.0',
:filename => '~/sites/yoursite.example/nzbwatchlist.xml',
:url => 'http://yoursite.example/nzbwatchlist.rss',
:title => 'NZBMatrix Watcher',
:description => 'NZBMatrix Watcher mails converted to RSS'
},
}
end
def prompt
@@config = defaults
@@config.each_pair do |key,v|
puts "Configuring section #{key}"
if v.kind_of?(Hash)
v.each_pair do |subkey,subvalue|
print "Please supply value for #{key}->#{subkey}: (default: #{subvalue})"
new_value = gets
v[subkey] = (new_value.strip == '' ? subvalue : new_value.strip)
puts
end
else
print "Please supply value for #{key}: (default: #{v})"
new_value = gets
@@config[key] = (new_value.strip == '' ? v : new_value.strip)
puts
end
end
File.open(CONFIG_LOCATION, "w"){|f| f.write(@@config.to_yaml)}
end
end
end
class Parser
attr_reader :config, :nzb_items, :status
def initialize
@config = Config.load
@nzb_items = []
@status = {}
@status = YAML.load_file(STATUS_LOCATION) if File.exist?(STATUS_LOCATION)
end
def retrieve_and_parse_mails
imap = Net::IMAP.new(config[:imap][:server])
imap.login(config[:imap][:user], config[:imap][:password])
imap.select(config[:imap][:folder])
imap.search(["FROM", config[:mailparser_sender], "BODY", "Download Link:"]).each do |msg_id|
status[:parsed_message_ids] = Set.new unless status.has_key?(:parsed_message_ids)
next if status[:parsed_message_ids].include?(msg_id)
msg = imap.fetch(msg_id, "(UID RFC822.SIZE ENVELOPE BODY[TEXT])")[0]
env = imap.fetch(msg_id, "ENVELOPE")[0].attr["ENVELOPE"]
date = imap.fetch(msg_id, "INTERNALDATE")[0].attr["INTERNALDATE"]
from = env.from[0].name
subject = env.subject
body = msg.attr["BODY[TEXT]"].gsub("\n"," ").gsub("\t", " ").gsub(13.chr," ").squeeze(" ")
#Parse format 1
body =~ /Download Link: ?<a href="([^"]+)"[^>]+>(.+?)<\/a><br>/
download_link = $1
description = $2
#Fallback to Parse format 2
unless download_link && description
body =~ /Download Link: ?(.+?)<([^>]+?)>/
download_link = $2
description = $1
end
unless download_link && description
puts "Could not parse this message: "
puts "From: #{from}"
puts "Subject: #{subject}"
puts "Body:"
puts '-' * 10
puts body
puts '-' * 10
puts "Description: #{description}"
puts "Download link: #{download_link}"
puts '-' * 80
next
end
#Remove redirects
download_link.gsub!(/https?:\/\/nzbmatrix\.com\/redirect\.php\?url=/,'')
#Force HTTPS links
download_link.gsub('http://','https://')
debug description
debug download_link
debug '-' * 80
@nzb_items << {
:from => from,
:subject => subject,
:description => description,
:download_link => download_link,
:date => date
}
status[:parsed_message_ids] << msg_id
end
imap.logout
# unless imap.disconnected?
# imap.disconnect
# end
end
def build_rss
content = RSS::Maker.make(config[:rss][:version]) do |m|
m.channel.title = config[:rss][:title]
m.channel.link = config[:rss][:url]
m.channel.description = config[:rss][:description]
m.items.do_sort = true # sort items by date
nzb_items.each do |nzb_item|
i = m.items.new_item
i.title = nzb_item[:description]
i.link = nzb_item[:download_link]
i.date = Time.parse(nzb_item[:date])
end
end
File.open(File.expand_path(config[:rss][:filename]),"w") do |f|
f.write(content)
end
end
def download_new_nzbs
nzb_watchfolder = File.expand_path(config[:nzb_watchfolder])
FileUtils.mkdir_p(nzb_watchfolder) unless File.exist?(nzb_watchfolder)
status[:downloaded_nzb_ids] = Set.new unless status.has_key?(:downloaded_nzb_ids)
nzb_items.each do |nzb_item|
nzb_id = ''
if nzb_item[:download_link] =~ /id=(\d+)/
nzb_id = $1
end
download_link = "https://api.nzbmatrix.com/v1.1/download.php?id=#{nzb_id}&username=#{config[:nzbmatrix_api][:username]}&apikey=#{config[:nzbmatrix_api][:key]}"
filename = nzb_item[:description].gsub(/[^a-zA-Z0-9_-]/,'_') + '-' + nzb_id + '.nzb'
filepath = File.expand_path(File.join(nzb_watchfolder,filename))
unless File.exist?(filepath)
status[:downloaded_nzb_ids] << nzb_id if `wget -q --no-check-certificate "#{download_link}" -O #{filepath}`
end
end
end
def store_status
File.open(STATUS_LOCATION,"w") {|f| f.write(status.to_yaml)}
end
end
parser = Parser.new
parser.retrieve_and_parse_mails
# parser.build_rss
parser.download_new_nzbs
parser.store_status
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment