#!/usr/bin/ruby # # Interface de jeu en ligne de commande pour MountyHall: http://www.mounthall.com/ # par Tyrollus , 2005 # (use with irb) require 'libhttpclient' require 'mountycreds' class Mounty attr_reader :dla, :pa, :x, :y, :n, :lvl, :pi, :px1, :px2 attr_reader :now attr_reader :http_client def Mounty.play m = Mounty.new m.login m.profil m end def initialize @http_client = HttpClient.new('games.mountyhall.com') # @http_client = HttpClient.new('213.186.53.57') @dbg_dir = '.mh_cache/botdbg/' i = 0 while File.exists? @dbg_dir+i.to_s i += 1 end @dbg_dir += "#{i}/" Dir.mkdir @dbg_dir end def save_page(url, page) base = @dbg_dir + url.gsub(/^\//, '').gsub('/', '.').gsub('&', '8').gsub(/[;'"]/, '_') i = 0 while File.exists? base + "-#{i}.html" i += 1 end File.open(base + "-#{i}.html", 'w') { |fd| fd.write page.content } end def get(url, *args) p = @http_client.get(url, *args) save_page url, p p end def post(url, *args) p = @http_client.post(url, *args) save_page 'post_'+url, p p end def get_enc(page, params, *args) page += '?' + params.map { |k, v| @http_client.urlenc(k) + '=' + @http_client.urlenc(v) }.join('&') unless params.empty? get(page, *args) end def profil p = get('/mountyhall/MH_Play/Play_profil.php') @dla, @pa, @x, @y, @n, @lvl, @pi, @px1, @px2 = nil go = nil p.parse.map { |e| e.attr['content'] if e.type == 'String' }.compact.each { |e| ggo = go go = nil case ggo when :DLA @dla = Time.mktime($3, $2, $1, $4, $5, $6) if e =~ %r{(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)} when :PA @pa = $1.to_i if e =~ /(\d+) PA/ when :pos @x, @y, @n = $1.to_i, $2.to_i, $3.to_i if e =~ /X = (-?\d+) \| Y = (-?\d+) \| N = (-?\d+)/ else case e when /Date Limite d'Action/ ; go = :DLA when /Il me reste/ ; go = :PA when /Position/ ; go = :pos when /Niveau.*: (\d+) \((\d+) PI\)/ @lvl, @pitot = $1.to_i, $2.to_i when /PX.*: (\d+) .* PX Personnels.*: (\d+)/ @px1, @px2 = $1.to_i, $2.to_i when /PI.*: (\d+) \(/ @pi = $1.to_i when %r{Heure Serveur : (\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)} @now = Time.mktime($3, $2, $1, $4, $5, $6) end end } self end def messages p = get('/mountyhall/Messagerie/MH_Messagerie.php') end def lieu p = get('/mountyhall/MH_Lieux/Lieu_Description.php') end def BM p = get('/mountyhall/MH_Play/Play_BM.php') end def evenements p = get('/mountyhall/MH_Play/Play_evenement.php') end def vue p = get('/mountyhall/MH_Play/Play_vue.php') intd = false p.parse.map { |e| case e.type when 'td' ; intd = true ; false when '/td' ; intd = false ; "\t" when '/tr', '/table' ; '' when 'String' ; e.attr['content'] if intd end }.compact end def actions p = get('/mountyhall/MH_Play/Play_action.php') @actions_form = nil @actions_values = Hash.new @actions_labels = Hash.new curlabel = nil p.parse.each { |e| case e.type when 'form' ; @actions_form = e.attr['action'] when 'input' @actions_values[e.attr['name']] = e.attr['value'] if e.attr['name'] and e.attr['value'] when 'option' curlabel = @actions_labels[e.attr['value'].to_i] = '' if e.attr['value'].length > 0 when 'String' if curlabel curlabel.replace(e.attr['content'].gsub(' ', "\xa0")) curlabel = nil end end } @actions_labels end def show_actions puts actions.sort.map { |k, v| "#{k.to_s.ljust(3)}: #{v}" } end def try_action(a) return false unless @actions_form raise "invalid action #{a.inspect}, not in #{@actions_labels.inspect}" unless @actions_labels[a] @actions_values['ai_ToDo'] = a.to_s @actions_values['as_SelectName'] = @actions_labels[a] get_enc(@actions_form, @actions_values) end def show_form(p, onlystr = false) inform = false nl = false p.parse.each { |e| case e.type when 'form' puts e unless onlystr inform = true when '/form' puts e, '' unless onlystr inform = false end next unless inform case e.type when 'String' print e.attr['content'].gsub(/( |\s)+/, ' ') nl = true when 'b', '/b', 'span', '/span', 'font', '/font' when 'br', 'p', 'table', '/table', 'tr', '/tr', 'td', '/td', 'tbody', '/tbody', 'div', '/div' puts if nl nl = false else puts if nl nl = false puts e unless onlystr end } nil end def entrainement actions try_action(5) pst = @http_client.post_allowed[0] p = post(pst.url, {}) show_form(p, true) pst = @http_client.post_allowed[0] post(pst.url, {}) get 'Play_menu.php' p end def amelioration_comp(id) # CdM: 16 # Insultes: 18 # Pistage: 21 # Miner: 29 # Tailler: 30 actions try_action(6) url = '/mountyhall/MH_Play/Actions/Play_a_Amelioration2.php' @http_client.get_url_allowed << url get(url) p = post('/mountyhall/MH_Play/Actions/Play_a_Amelioration3b.php', {'ai_IdComp' => id.to_s}) show_form(p) pst = @http_client.post_allowed[0] post(pst.url, {}) get 'Play_menu.php' end # no arg = list targets def comp_target(idx, t = nil) if not t or not @targets or not @targets.include? t actions try_action(idx) @targets = @http_client.post_allowed[0].vars['ai_IdTarget'] # IdT: ai_IDTarget else @targets = nil pst = @http_client.post_allowed[0] p = post(pst.url, {'ai_IdTarget' => t}) show_form(p, true) pst = @http_client.post_allowed[0] post(pst.url, {}) get 'Play_menu.php' p end end def projectile(t = nil) comp_target(201, t) end def camouflage actions show_form try_action(104) end def login cookiefile = '.mounty.cookie' if File.exists?(cookiefile) File.readlines(cookiefile).each { |l| c = l.chomp.split(/\s*=\s*/, 2) @http_client.cookie[c[0]] = c[1] } p = get '/mountyhall/MH_Play/Play_profil.php' if @http_client.cur_url =~ /SessionExpired/i puts 'session expired!' logout return login end puts 'resumed' else get '/' p = post('/mountyhall/MH_Play/PlayStart.php', {'as_Login' => @@login, 'as_Pwd' => @@pass, 'ai_design' => '20', 'ab_activation' => '1', 'as_Action' => 'Entrer dans le Monde Souterrain'}) File.open(cookiefile, 'w') { |fd| @http_client.cookie.each { |k, v| fd.puts "#{k} = #{v}" } } show_form(p, true) # TODO post post_allowed[0], handle frames end true end def logout @http_client.get_url_allowed << '/mountyhall/Logout.php' File.unlink('.mounty.cookie') if File.exists?('.mounty.cookie') get('/mountyhall/Logout.php') @http_client.clear end def inspect "troll lvl #@lvl, dla #{@dla.strftime('%d/%m %H:%M') if @dla}, #@pa PA - pos (#@x, #@y, #@n)" end end