public
Last active

  • Download Gist
p0wn1e_bot.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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 180 181 182 183 184 185
### 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!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.