class Account(): ''' Manages the requests for a specific account, handles locking etc. With this requests are issued. ''' default_headers = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/5.0", "Accept": "text/javascript, text/html,application/xhtml+xml,application/xml, */*;q=0.9,*/*;q=0.8", "Accept-Language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3", "Connection": "close" } def __init__(self, server, user_name): assert isinstance(server, tuple) and len(server) == 2 self.server = server self.user_name = user_name self.url = "http://ts%s.travian.%s" % server self.session = requests.Session() self.villages = {} self.hero = Hero() self.production_boost = None ### DB-loading ### def get_db(self): return db.users.find_one({'name': self.user_name, 'server':self.server}) def init_db(self, email, password, nation, proxies): ''' Sets the basic parameters for this account ''' user = {'name': self.user_name, 'email': email, 'password': password, 'server': self.server, 'nation': nation, 'proxies': proxies, 'activated': False } db.users.save(user) def load_db(self): user = self.get_db() if not user: return self.proxies = user['proxies'] self.email = user['email'] self.password = user['password'] self.nation = user['nation'] self.activated = user['activated'] # TODO: better proxy selection if len(self.proxies): self.session.proxies = { "http": self.proxies[0], "https": self.proxies[0] } ### Request functions ### def request_GET(self, url, createDom=True): headers = self.default_headers result = self.session.get(self.url + url, headers=headers) if not result.text: raise ValueError("result.text is empty!") if createDom: return HtmlDom().createDom(result.text) return result def request_POST(self, url, params, add_headers = {}, createDom=True): headers = dict(self.default_headers) headers['Content-Type'] = 'application/x-www-form-urlencoded' headers.update(add_headers) # perform login result = self.session.post(self.url + url, data=params, headers=headers) logger.log_note("POST request", "POST issued for %s with %s" % (url, params)) try: if createDom: return HtmlDom().createDom(result.text) except: logger.log_error("POST failed", result.text, title="failed reading page in POST %s" % url) return result reg_input_name = re.compile(r'.*name="([^"]*)".*') reg_input_value = re.compile(r'.*value="([^"]*)".*') reg_input_type = re.compile(r'.*type="([^"]*)".*') def submit_form(self, doc, form_id, form_values, form_action = None): if form_action is None: form_action = "/" + doc.find(form_id).attr("action") inputs = doc.find("%s input" % form_id) params = {} class _Dummy(): __slots__ = () def group(self, n): return "" for inpt in inputs: name = (self.reg_input_name.match(inpt.html()) or _Dummy()).group(1) value = (self.reg_input_value.match(inpt.html()) or _Dummy()).group(1) type1 = (self.reg_input_type.match(inpt.html()) or _Dummy()).group(1) params[name] = value params.update(form_values) return self.request_POST(form_action, params) def ajax_cmd(self, cmd, params, get_cmd='quest'): params["cmd"] = cmd params["ajaxToken"] = self.ajax_token headers = dict(self.default_headers) headers.update({"Content-Type": "application/x-www-form-urlencoded;"}) response = self.session.post(self.url + "/ajax.php?cmd=%s" % get_cmd, data=params, headers=headers) return response ### Cookies ### def save_cookie(self): user_db = self.get_db() if user_db is None: raise ValueError(self.user_name) user_db['cookies'] = { k: self.session.cookies._find(k) for k, v in self.session.cookies.items() } db.users.save(user_db) def load_cookie(self): user_db = self.get_db() if 'cookies' in user_db: self.session.cookies.update(user_db['cookies']) return True else: return False def clear_cookie(self): self.session.cookies.clear() ### Basic actions (activation, login, ..) def perform_activation(self, sector): self.load_cookie() user_db = self.get_db() action.action_activate(self, self.server[0], user_db['activation_code']) self.login() self.save_cookie() action.action_select_spawn(self, self.nation, sector) self.loadup() # requests 1 village quest.skip_tutorial(self) action.action_quest(self, "next", "Tutorial_15a") user_db['activated'] = True db.users.save(user_db) def login(self, doc_login = None): logger.log_info("login", "logging in...") self.clear_cookie() doc = doc_login or self.request_GET("/dorf1.php") # retrieve current login POST data params = {} for inpt in doc.find("form[name=login] input"): name = inpt.attr('name') value = None if inpt._is("[value]"): value = inpt.attr('value') params[name] = value # alter it params['name'] = self.user_name params['password'] = self.password params['lowRes'] = '1' params['s1'] = 'Einloggen' params['w'] = '1920:1080' # perform login and return village overview doc = self.request_POST("/dorf1.php", params) self.ajax_token = reader.read_ajax_token(doc) return doc def logout(self): self.request_GET("/logout.php", createDom=False) def loadup(self): """ Loads cookies, Logins, Loads all villages, events and the hero. """ self.load_db() self.load_cookie() doc_resources = self.request_GET("/dorf1.php") if not len(reader.read_resource_fields(doc_resources)): self.clear_cookie() doc_resources = self.login(doc_login = doc_resources) self.save_cookie() if not len(reader.read_resource_fields(doc_resources)): logger.log_error("login failed", "The login failed") else: self.ajax_token = reader.read_ajax_token(doc_resources) village = self.request_village(None, doc_resources) other_villages = self.villages.keys() ^ { village.village_id } # all villages except the active self.request_villages(other_villages) self.request_hero() self.production_boost = reader.read_production_boost(doc_resources) def request_village(self, village_id, doc_login = None): ''' If village_id is None, doc_login will be used and a list of villages will be stored. ''' doc_resources = doc_login or self.request_GET("/dorf1.php?newdid=%d" % village_id) pages = { 'resources': doc_resources, 'village': self.request_GET("/dorf2.php") } active_village = reader.read_villages(doc_resources, True)[0] if village_id is None: all_villages = reader.read_villages(doc_resources) self.villages.clear() self.villages.update( { vill['village_id']: Village(self, **vill) for vill in all_villages } ) if active_village['village_id'] not in self.villages: self.villages[active_village['village_id']] = Village(self, **active_village) self.current_village = self.villages[active_village['village_id']] village = self.villages[active_village['village_id']] if active_village['name'] != village.name: logger.log_info("village renamed", "Village %s renamed to %s" % (village.name, active_village['name'])) village.name = active_village['name'] village.read_content(pages) village.read_events(pages) village.new_refresh_time() return village def request_villages(self, village_keys=None): """ Requests all villages, or only those with the given keys. Writes the village data into existing village instances or creates new ones. """ village_keys = village_keys if village_keys is not None else self.villages.keys() for village_id in village_keys: self.request_village(village_id) def request_hero(self): doc = self.request_GET('/hero_inventory.php') reader.read_hero(doc, self.hero) def get_state(self): return AccountState(self.production_boost, self.hero.get_state()) def update(self): for vill in self.villages.values(): vill.update()