Skip to content

Instantly share code, notes, and snippets.

@hellekin
Created September 9, 2009 01:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hellekin/183385 to your computer and use it in GitHub Desktop.
Save hellekin/183385 to your computer and use it in GitHub Desktop.
### P0wn1e now lives in http://github.com/hellekin/p0wn1e
#!/usr/bin/env ruby1.8
#
# p0wn1e is the hackerspaces.org notifier.
# It reads an ATOM feed and send updates to identi.ca
#
################################################################################
### BEGIN CONFIGURATION
$options = {}
# Identi.ca credentials
$options[:username] = 'p0wn1e'
$options[:password] = 'I_can_haz_sekrit?'
# URI of the ATOM feed for the wiki pages
$options[:feed_uri] = 'http://hackerspaces.org/w/index.php?title=Special:NewPages&feed=atom'
# Uncomment the following line to turn on debugging output
$DEBUG = true
### END CONFIGURATION
################################################################################
%w(rubygems atom net/https yaml).each { |lib| require(lib) }
class P0wn1e
# sleep time in seconds
@@delay = 42*2
# dent message prototype
@@dent = "!hs %s created '%s' at %s"
# and corresponding regex prototype
@@dentr = "!hs.*created\s.(.+).\sat.*"
# P0wn1e haz atomz
DENT_URI = "http://identi.ca/api/statuses/user_timeline/__USER__.atom"
# P0wn1e posts updates to identi.ca
POST_URI = URI.parse('https://identi.ca/api/statuses/update.xml')
def initialize(options = {})
@user = options[:username]
@pass = options[:password]
@feed_uri = options[:feed_uri]
@all_dents = []
@all_pages = []
@new_pages = []
debug("initialization complete")
end
# Run p0wn1e! Run!
def run!
@http = http_post_connect
loop do
refresh_all_feeds
update if pending_updates?
debug("haz no pending updates... Sleeping 10 minutes")
sleep 600 # sleep 10 minutes between batches
end
rescue Exception => e
debug("failed to start! [#{e.class}] #{e.message}")
raise e
end
# We only send to identi.ca as the account will forward to twitter
def dent(message)
debug("denting #{message}")
req = Net::HTTP::Post.new(POST_URI.path)
req.form_data=({ 'status' => message })
req.basic_auth(@user, @pass)
debug("authenticated")
res = @http.request(req)
debug("sent request")
if res.body =~ %r{<text>#{@@dentr}</text>.*<id>(\d+)</id>}
@last_update = [$1, $2.to_i]
debug("sent new message: #{@last_update.inspect} #{message}")
end
res
rescue Exception => e
error = "dent failed for message: #{message}\nwith error [#{e.class}] #{e.message}"
debug(error)
sleep @@delay
retry
end
def last_update
debug("last_update: #{@last_update.inspect}")
@last_update ||= find_last_update
end
def new_pages
candidates = @all_pages.last.map { |p| [p.title, p.authors.first.name, p.links.first.href] }.reverse
start_idx = 0
last_dent_page_name = last_update.first
candidates.each_with_index do |c, i|
if c.first == last_dent_page_name
start_idx = [i+1, candidates.length].min
break
end
end
debug("new_pages start at #{start_idx} / #{candidates.size}")
candidates[start_idx..candidates.length]
end
def pending_updates?
!@all_dents.map(&:first).include?(@all_pages.first)
end
private
# Return an <tt>Atom::Feed</tt> object from a given +url+
def atom_read(url)
Atom::Feed.new(Net::HTTP.get(URI.parse(url)))
end
# Print a debugging message to +STDERR+
def debug(message = "debug")
$stderr.puts("p0wn1e " << message) if $DEBUG
end
# Return [ page_name, notice_id ] from upstream
def find_last_update
read_identica_feed.first
end
# Open HTTP connection to identi.ca API for posting updates
def http_post_connect
debug("preparing HTTPS connection to POST")
http = Net::HTTP.new(POST_URI.host, POST_URI.port)
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.use_ssl = true
http
end
# Return an Array of [ page_name, notice_id ]
# for all entries corresponding to wiki new pages dents
def read_identica_feed
feed = atom_read(DENT_URI.sub(/__USER__/,@user))
debug("got feed #{feed.title} with #{feed.entries.size} entries")
feed.entries.map do |entry|
if entry.title =~ %r{^#{@@dentr}$}
[ $1, entry.id.sub(/.*\//,'').to_i ]
end
end.compact || []
rescue Exception => e
debug("P0wn1e can't haz atoms: [#{e.class}] #{e.message}")
sleep(@@delay)
retry
end
# Return an Array of [ last_updated_page_name, all_entries ]
def read_new_pages_feed
feed = atom_read(@feed_uri)
debug("got feed #{feed.title} with #{feed.entries.size} entries")
[feed.entries.first.title, feed.entries]
rescue Exception => e
error = "P0wn1e can't haz atoms: [#{e.class}] #{e.message}"
sleep(@@delay)
retry
end
# Fetch identi.ca and wiki atom feeds from upstream
def refresh_all_feeds
debug("refreshing identi.ca feed")
@all_dents = read_identica_feed
@last_update = @all_dents.first || ['',0]
debug("refreshing new pages feed")
@all_pages = read_new_pages_feed
debug("computing new pages")
@new_pages = new_pages
debug("refresh_all_feeds done")
end
# Send updates about all newly created pages since last check
def update
debug("starting update")
while (page = @new_pages.shift) != nil
page_name, author, link = page
dent(@@dent % [author, page_name, link])
# debug(@@dent % [author, page_name, link])
sleep(@@delay) # Don't post too often, but post all!
end
debug("done updating")
end
end
# Run P0wn1e! Run!
P0wn1e.new($options).run!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment