class Bot(object): BASE_URL = 'https://en.ogame.gameforge.com/' LOGIN_URL = 'https://en.ogame.gameforge.com/main/login' HEADERS = [('User-agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64)\ AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15')] RE_BUILD_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\', null, 1\)") RE_RESEARCH_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\'") RE_SERVER_TIME = re.compile( r"var serverTime=new Date\((.*)\);var localTime") #ship -> ship id on the page SHIPS = { 'lm': '204', 'hm': '205', 'cr': '206', 'ow': '207', 'pn': '215', 'bb': '211', 'ns': '213', 'gs': '214', 'lt': '202', 'dt': '203', 'cs': '208', 'rc': '209', 'ss': '210' } # mission ids MISSIONS = { 'attack': '1', 'transport': '3', 'station': '4', 'expedition': '15', 'collect': '8' } TARGETS = {'planet': '1', 'moon': '3', 'debris': '2'} SPEEDS = { 100: '10', 90: '9', 80: '8', 70: '7', 60: '6', 50: '5', 40: '4', 30: '3', 20: '2', 10: '1' } def __init__(self, username=None, password=None, uni='69'): self.uni = uni self.username = username self.password = password self.logged_in = False self._prepare_logger() self._prepare_browser() farms = options['farming']['farms'] self.farm_no = randint(0, len(farms) - 1) if farms else 0 self.MAIN_URL = 'https://s%s-en.ogame.gameforge.com/game/index.php' % self.uni self.PAGES = { 'main': self.MAIN_URL + '?page=overview', 'resources': self.MAIN_URL + '?page=resources', 'station': self.MAIN_URL + '?page=station', 'research': self.MAIN_URL + '?page=research', 'shipyard': self.MAIN_URL + '?page=shipyard', 'defense': self.MAIN_URL + '?page=defense', 'fleet': self.MAIN_URL + '?page=fleet1', 'galaxy': self.MAIN_URL + '?page=galaxy', 'galaxyCnt': self.MAIN_URL + '?page=galaxyContent', 'events': self.MAIN_URL + '?page=eventList', } self.planets = [] self.moons = [] self.active_attacks = [] self.fleet_slots = 0 self.active_fleets = 0 self.server_time = self.local_time = datetime.now() self.time_diff = 0 self.emergency_sms_sent = False self.transport_manager = TransportManager() self.sim = Sim() def _get_url(self, page, planet=None): url = self.PAGES[page] if planet is not None: url += '&cp=%s' % planet.id return url def _prepare_logger(self): self.logger = logging.getLogger("mechanize") fh = RotatingFileHandler('bot.log', maxBytes=100000, backupCount=5) sh = logging.StreamHandler() fmt = logging.Formatter(fmt='%(asctime)s %(levelname)s %(message)s', datefmt='%m-%d, %H:%M:%S') fh.setFormatter(fmt) sh.setFormatter(fmt) self.logger.setLevel(logging.INFO) self.logger.addHandler(fh) self.logger.addHandler(sh) def _prepare_browser(self): self.br = mechanize.Browser() self.br.set_handle_equiv(True) self.br.set_handle_redirect(True) self.br.set_handle_referer(True) self.br.set_handle_robots(False) self.br.addheaders = self.HEADERS def _parse_build_url(self, js): """ convert: `sendBuildRequest('url', null, 1)`; into: `url` """ return self.RE_BUILD_REQUEST.findall(js)[0] def _parse_research_url(self, js): """ convert: `sendBuildRequest('url', null, false)`; into: `url` """ return self.RE_RESEARCH_REQUEST.findall(js)[0] def _parse_server_time(self, content): return self.RE_SERVER_TIME.findall(content)[0] def get_mother(self): for p in self.planets: if p.mother: return p return p[0] if self.planets else None def get_closest_planet(self, p): def min_dist(p, d): return d _, d, _ = p.split(":") return sorted([(planet, planet.get_distance(p)) for planet in self.planets], key=lambda x: x[1])[0][0] def find_planet(self, name=None, coords=None, id=None, is_moon=None): if is_moon: planets = self.moons else: planets = self.planets for p in planets: if name == p.name or coords == p.coords or id == p.id: return p def get_safe_planet(self, planet): ''' Get first planet which is not under attack and isn't `planet` ''' unsafe_planets = [a.planet for a in self.active_attacks] for p in self.planets: if not p in unsafe_planets and p != planet: return p # no safe planets! go to mother return self.planets[0] def login(self, username=None, password=None): username = username or self.username password = password or self.password try: resp = self.br.open(self.MAIN_URL, timeout=10) soup = BeautifulSoup(resp) except: return False alert = soup.find(id='attack_alert') # no redirect on main page == user logged in if resp.geturl() != self.BASE_URL and alert: self.logged_in = True self.logger.info('Logged as: %s' % username) return True self.logger.info('Logging in..') self.br.select_form(name='loginForm') # self.br.form['uni'] = ['s%s-en.ogame.gameforge.com' % self.uni] # self.br.form['login'] = username # self.br.form['pass'] = password self.br.form.set_value_by_label(['Hyperion'], 'uni') self.br.form.set_value(username, 'login') self.br.form.set_value(password, 'pass') self.br.submit() if self.br.geturl().startswith(self.MAIN_URL): self.logged_in = True self.logger.info('Logged as: %s' % username) return True else: self.logged_in = False self.logger.error('Login failed!') return False def calc_time(self, resp): try: y, mo, d, h, mi, sec = map( int, self._parse_server_time(resp).split(',')) except: self.logger.error('Exception while calculating time') else: self.local_time = n = datetime.now() self.server_time = datetime(n.year, n.month, n.day, h, mi, sec) self.time_diff = self.server_time - self.local_time self.logger.info('Server time: %s, local time: %s' % \ (self.server_time, self.local_time)) def fetch_planets(self): self.logger.info('Fetching planets..') resp = self.br.open(self.PAGES['main']).read() self.calc_time(resp) soup = BeautifulSoup(resp) self.planets = [] self.moons = [] try: for i, c in enumerate(soup.findAll('a', 'planetlink')): name = c.find('span', 'planet-name').text coords = c.find('span', 'planet-koords').text[1:-1] url = c.get('href') p_id = int(c.parent.get('id').split('-')[1]) construct_mode = len(c.parent.findAll('a', 'constructionIcon')) != 0 p = Planet(p_id, name, coords, url, construct_mode) if i == 0: p.mother = True self.planets.append(p) #check if planet has moon moon = c.parent.find('a', 'moonlink') if moon and 'moonlink' in moon['class']: url = moon.get('href') m_id = url.split('cp=')[1] m = Moon(m_id, coords, url) self.moons.append(m) except: self.logger.exception('Exception while fetching planets') else: self.check_attacks(soup) def handle_planets(self): self.fetch_planets() for p in iter(self.planets): self.update_planet_info(p) self.update_planet_research(p) self.update_planet_facilities(p) self.update_planet_fleet(p) for m in iter(self.moons): self.update_planet_info(m) self.update_planet_fleet(m) def update_planet_fleet(self, planet): resp = self.br.open(self._get_url('fleet', planet)) soup = BeautifulSoup(resp) ships = {} for k, v in self.SHIPS.iteritems(): available = 0 try: s = soup.find(id='button' + v) available = int( s.find('span', 'textlabel').nextSibling.replace('.', '')) except: available = 0 ships[k] = available #self.logger.info('Updating %s fleet' % planet) #self.logger.info('%s' % fleet) planet.ships = ships def update_planet_info(self, planet): in_construction_mode = False resp = self.br.open(self._get_url('resources', planet)) soup = BeautifulSoup(resp) try: metal = int(soup.find(id='resources_metal').text.replace('.', '')) planet.resources['metal'] = metal crystal = int( soup.find(id='resources_crystal').text.replace('.', '')) planet.resources['crystal'] = crystal deuterium = int( soup.find(id='resources_deuterium').text.replace('.', '')) planet.resources['deuterium'] = deuterium energy = int( soup.find(id='resources_energy').text.replace('.', '')) planet.resources['energy'] = energy except: self.logger.exception('Exception while updating resources info') else: self.logger.info('Updating resources info for %s:' % planet) s = 'metal - %(metal)s, crystal - %(crystal)s, deuterium - %(deuterium)s' self.logger.info(s % planet.resources) if planet.is_moon(): return try: buildingList = soup.find(id='building') storageList = soup.find(id='storage') buildings = ('metalMine', 'crystalMine', 'deuteriumMine', 'solarPlant', 'fusionPlant', 'solarSatellite', 'metalStorage', 'crystalStorage', 'deuteriumTank') storages = ('metalStorage', 'crystalStorage', 'deuteriumTank') allBuildingList = zip(buildings, buildingList.findAll('li')) + zip( storages, storageList.findAll('li')) for building, b in allBuildingList: can_build = 'on' in b.get('class') fb = b.find('a', 'fastBuild') build_url = fb.get('onclick') if fb else '' if build_url: build_url = self._parse_build_url(build_url) try: level = int(b.find('span', 'textlabel').nextSibling) except AttributeError: try: level = int(b.find('span', 'level').text) except: pass suff_energy = planet.resources[ 'energy'] - self.sim.upgrade_energy_cost( building, level + 1) > 0 res = dict(level=level, can_build=can_build, build_url=build_url, sufficient_energy=suff_energy) planet.buildings[building] = res if buildingList.find('div', 'construction'): in_construction_mode = True except: self.logger.exception('Exception while updating buildings info') return False else: self.logger.info('%s buildings were updated' % planet) if not in_construction_mode: text, url = planet.get_mine_to_upgrade() if url: self.logger.info('Building upgrade on %s: %s' % (planet, text)) self.br.open(url) planet.in_construction_mode = True #let now transport manager to clear building queue self.transport_manager.update_building(planet) else: self.logger.info('Building queue is not empty') return True def update_planet_research(self, planet): resp = self.br.open(self._get_url('research', planet)) soup = BeautifulSoup(resp) try: ButtonList = soup.find(id='buttonz') AllResearchList = ButtonList.findAll('li') for research in AllResearchList: if research.get('class') == 'on': fb = research.find('a', 'fastBuild') if fb: build_url = fb.get('onclick') if fb else '' build_url = self._parse_research_url(build_url) self.logger.info('Research launched on %s:%s' % (planet, fb.get('title'))) self.br.open(build_url) break except: self.logger.exception('Exception while retrieving researches') def update_planet_facilities(self, planet): resp = self.br.open(self._get_url('station', planet)) soup = BeautifulSoup(resp) try: ButtonList = soup.find(id='stationbuilding') AllResearchList = ButtonList.findAll('li') for research in AllResearchList: if research.get('class') == 'on': fb = research.find('a', 'fastBuild') if fb: build_url = fb.get('onclick') if fb else '' build_url = self._parse_research_url(build_url) self.logger.info('Facility upgraded on %s:%s' % (planet, fb.get('title'))) self.br.open(build_url) break except: self.logger.exception( 'Exception while retrieving facilities statuses') return True def transport_resources(self): tasks = self.transport_manager.find_dest_planet(self.planets) if tasks is None: return False self.logger.info(self.transport_manager.get_summary()) for task in iter(tasks): self.logger.info('Transport attempt from: %s, to: %s with resources %s' \ % (task['from'], task['where'], task['resources'])) result = self.send_fleet( task['from'], task['where'].coords, fleet=task['from'].get_fleet_for_resources(task['resources']), resources=task['resources'], mission='transport') if result: self.transport_manager.update_sent_resources(task['resources']) self.logger.info('Resources sent: %s, resources needed: %s' \ % (task['resources'], self.transport_manager.get_resources_needed())) return True def build_defense(self, planet): """ Build defense for all resources on the planet 1. plasma 2. gauss 3. heavy cannon 4. light cannon 5. rocket launcher """ url = self._get_url('defense', planet) resp = self.br.open(url) for t in ('406', '404', '403', '402', '401'): self.br.select_form(name='form') self.br.form.new_control('text', 'menge', {'value': '100'}) self.br.form.fixup() self.br['menge'] = '100' self.br.form.new_control('text', 'type', {'value': t}) self.br.form.fixup() self.br['type'] = t self.br.form.new_control('text', 'modus', {'value': '1'}) self.br.form.fixup() self.br['modus'] = '1' self.br.submit() def get_player_status(self, destination, origin_planet=None): if not destination: return status = {} origin_planet = origin_planet or self.get_closest_planet(destination) galaxy, system, position = destination.split(':') url = self._get_url('galaxyCnt', origin_planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) soup.find(id='galaxytable') planets = soup.findAll('tr', {'class': 'row'}) target_planet = planets[int(position) - 1] name_el = target_planet.find('td', 'playername') status['name'] = name_el.find('span').text status['inactive'] = 'inactive' in name_el.get('class', '') return status def find_inactive_nearby(self, planet, radius=15): self.logger.info("Searching idlers near %s in radius %s" % (planet, radius)) nearby_systems = planet.get_nearby_systems(radius) idlers = [] for system in nearby_systems: galaxy, system = system.split(":") url = self._get_url('galaxyCnt', planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) galaxy_el = soup.find(id='galaxytable') planets = galaxy_el.findAll('tr', {'class': 'row'}) for pl in planets: name_el = pl.find('td', 'playername') debris_el = pl.find('td', 'debris') inactive = 'inactive' in name_el.get('class', '') debris_not_found = 'js_no_action' in debris_el.get('class', '') if not inactive or not debris_not_found: continue position = pl.find('td', 'position').text coords = "%s:%s:%s" % (galaxy, system, position) player_id = name_el.find('a').get('rel') player_info = soup.find(id=player_id) rank_el = player_info.find('li', 'rank') if not rank_el: continue rank = int(rank_el.find('a').text) if rank > 4000 or rank < 900: continue idlers.append(coords) time.sleep(2) return idlers def find_inactives(self): inactives = [] for p in self.planets: try: idlers = self.find_inactive_nearby(p) self.logger.info(" ".join(idlers)) inactives.extend(idlers) except Exception as e: self.logger.exception(e) continue time.sleep(5) self.logger.info(" ".join(inactives)) self.inactives = list(set(inactives)) self.logger.info(inactives) def send_fleet(self, origin_planet, destination, fleet={}, resources={}, mission='attack', target='planet', speed=None): if origin_planet.coords == destination: self.logger.error('Cannot send fleet to the same planet') return False self.logger.info('Sending fleet from %s to %s (%s)' \ % (origin_planet, destination, mission)) resp = self.br.open(self._get_url('fleet', origin_planet)) try: try: self.br.select_form(name='shipsChosen') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False soup = BeautifulSoup(resp) for ship, num in fleet.iteritems(): s = soup.find(id='button' + self.SHIPS[ship]) num = int(num) try: available = int( s.find('span', 'textlabel').nextSibling.replace('.', '')) except: available = 0 if available < num and mission in ('attack', 'expedition'): self.logger.info('No available ships to send') return False if num > 0: self.br.form['am' + self.SHIPS[ship]] = str(num) self.br.submit() try: self.br.select_form(name='details') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False galaxy, system, position = destination.split(':') self.br['galaxy'] = galaxy self.br['system'] = system self.br['position'] = position self.br.form.find_control("type").readonly = False self.br['type'] = self.TARGETS[target] self.br.form.find_control("speed").readonly = False if speed: self.br['speed'] = self.SPEEDS[speed] self.br.submit() self.br.select_form(name='sendForm') self.br.form.find_control("mission").readonly = False self.br.form['mission'] = self.MISSIONS[mission] if 'metal' in resources: self.br.form['metal'] = str(resources['metal']) if 'crystal' in resources: self.br.form['crystal'] = str(resources['crystal']) if 'deuterium' in resources: self.br.form['deuterium'] = str(resources['deuterium']) self.br.submit() except Exception as e: self.logger.exception(e) return False else: if mission == 'attack': self.farm_no += 1 return True def send_message(self, url, player, subject, message): self.logger.info('Sending message to %s: %s' % (player, message)) self.br.open(url) self.br.select_form(nr=0) self.br.form['betreff'] = subject self.br.form['text'] = message self.br.submit() def send_sms(self, msg): from smsapigateway import SMSAPIGateway try: SMSAPIGateway().send(msg) except Exception as e: self.logger.exception(str(e)) def handle_attacks(self): attack_opts = options['attack'] send_sms = bool(options['sms']['send_sms']) for a in self.active_attacks: if a.is_dangerous(): self.logger.info('Handling attack: %s' % a) if not a.planet.is_moon(): self.build_defense(a.planet) if send_sms and not a.sms_sent: self.send_sms(a.get_sms_text()) a.sms_sent = True if send_sms and not a.message_sent: self.send_message(a.message_url, a.player, attack_opts['message_topic'], a.get_random_message()) a.message_sent = True self.fleet_save(a.planet) def check_attacks(self, soup): alert = soup.find(id='attack_alert') if not alert: self.logger.exception('Check attack failed') return if 'noAttack' in alert.get('class', ''): self.logger.info('No attacks') self.active_attacks = [] else: self.logger.info('ATTACK!') resp = self.br.open(self.PAGES['events']) soup = BeautifulSoup(resp) hostile = False try: for tr in soup.findAll('tr'): countDown = tr.find('td', 'countDown') if countDown and 'hostile' in countDown.get('class', ''): hostile = True # First: check if attack was noticed if tr.get('id'): attack_id = tr.get('id').split('-')[1] elif countDown.get('id'): attack_id = countDown.get('id').split('-')[2] if not attack_id or attack_id in [ a.id for a in self.active_attacks ]: continue try: # Attack first discovered: save attack info arrivalTime = tr.find( 'td', 'arrivalTime').text.split(' ')[0] coordsOrigin = tr.find('td', 'coordsOrigin') if coordsOrigin: if coordsOrigin.find('a'): coordsOrigin = coordsOrigin.find( 'a').text.strip()[1:-1] destCoords = tr.find('td', 'destCoords') if destCoords: destCoords = destCoords.find( 'a').text.strip()[1:-1] originFleet = tr.find('td', 'originFleet') detailsFleet = int( tr.find('td', 'detailsFleet').span.text.replace( '.', '')) player_info = originFleet.find('a') message_url = player_info.get('href') player = player_info.get('data-player-name') is_moon = False # TODO! planet = self.find_planet(coords=destCoords, is_moon=is_moon) a = Attack(planet, attack_id, arrivalTime, coordsOrigin, destCoords, detailsFleet, player, message_url) self.active_attacks.append(a) except Exception as e: self.logger.exception(e) self.send_sms('ATTACKEROR') if not hostile: self.active_attacks = [] except Exception as e: self.logger.exception(e) def fleet_save(self, p): if not p.has_ships(): return fleet = p.ships #recyclers are staying! #fleet['rc'] = 0 self.logger.info('Making fleet save from %s' % p) self.send_fleet(p, self.get_safe_planet(p).coords, fleet=fleet, mission='station', speed=10, resources={ 'metal': p.resources['metal'] + 500, 'crystal': p.resources['crystal'] + 500, 'deuterium': p.resources['deuterium'] + 500 }) def collect_debris(self, p): if not p.has_ships(): return self.logger.info('Collecting debris from %s using %s recyclers' % (p, p.ships['rc'])) self.send_fleet(p, p.coords, fleet={'rc': p.ships['rc']}, mission='collect', target='debris') def send_expedition(self): expedition = options['expedition'] planets = expedition['planets'].split(' ') random.shuffle(planets) for coords in planets[:3]: planet = self.find_planet(coords=coords) if planet: galaxy, system, position = planet.coords.split(':') expedition_coords = '%s:%s:16' % (galaxy, system) self.send_fleet(planet, expedition_coords, fleet={ expedition['ships_kind']: expedition['ships_number'] }, mission='expedition') def farm(self): farms = options['farming']['farms'].split(' ') ships_kind = options['farming']['ships_kind'] ships_number = options['farming']['ships_number'] l = len(farms) if l == 0 or not farms[0]: return farm = farms[self.farm_no % l] if not self.get_player_status(farm)['inactive']: self.farm_no += 1 self.logger.error('farm %s seems not to be inactive!', farm) return self.send_fleet(self.get_closest_planet(farm), farm, fleet={ships_kind: ships_number}) def sleep(self): sleep_options = options['general'] sleep_time = randint(0, int(sleep_options['seed'])) + int( sleep_options['check_interval']) self.logger.info('Sleeping for %s secs' % sleep_time) if self.active_attacks: sleep_time = 60 time.sleep(sleep_time) def stop(self): self.logger.info('Stopping bot') os.unlink(self.pidfile) def start(self): self.logger.info('Starting bot') self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) #main loop while True: if self.login(): try: self.handle_planets() #self.find_inactives() if not self.active_attacks: if True or not self.transport_resources(): self.send_expedition() self.farm() self.farm() else: self.handle_attacks() except Exception as e: self.logger.exception(e) #self.stop() #return else: self.logger.error('Login failed!') #self.stop() #return self.sleep()
class Bot(object): BASE_URL = 'http://pl.ogame.gameforge.com/' LOGIN_URL = 'http://pl.ogame.gameforge.com/main/login' HEADERS = [('User-agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64)\ AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15')] RE_BUILD_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\', null, 1\)") RE_SERVER_TIME = re.compile(r"var serverTime=new Date\((.*)\);var localTime") #ship -> ship id on the page SHIPS = { 'lm': '204', 'hm': '205', 'cr': '206', 'ow': '207', 'pn': '215', 'bb': '211', 'ns': '213', 'gs': '214', 'lt': '202', 'dt': '203', 'cs': '208', 'rc': '209', 'ss': '210' } # mission ids MISSIONS = { 'attack': '1', 'transport': '3', 'station': '4', 'expedition': '15', 'collect' : '8' } TARGETS = { 'planet' : '1', 'moon' : '3', 'debris' : '2' } SPEEDS = { 100: '10', 90: '9', 80: '8', 70: '7', 60: '6', 50: '5', 40: '4', 30: '3', 20: '2', 10: '1' } def __init__(self, username=None, password=None, uni='69'): self.uni = uni self.username = username self.password = password self.logged_in = False self._prepare_logger() self._prepare_browser() farms = options['farming']['farms'] self.farm_no = randint(0, len(farms)-1) if farms else 0 self.MAIN_URL = 'http://s%s-pl.ogame.gameforge.com/game/index.php' % self.uni self.PAGES = { 'main': self.MAIN_URL + '?page=overview', 'resources': self.MAIN_URL + '?page=resources', 'station': self.MAIN_URL + '?page=station', 'research': self.MAIN_URL + '?page=research', 'shipyard': self.MAIN_URL + '?page=shipyard', 'defense': self.MAIN_URL + '?page=defense', 'fleet': self.MAIN_URL + '?page=fleet1', 'galaxy': self.MAIN_URL + '?page=galaxy', 'galaxyCnt': self.MAIN_URL + '?page=galaxyContent', 'events': self.MAIN_URL + '?page=eventList', } self.planets = [] self.moons = [] self.active_attacks = [] self.fleet_slots = 0 self.active_fleets = 0 self.server_time = self.local_time = datetime.now() self.time_diff = 0 self.emergency_sms_sent = False self.transport_manager = TransportManager() self.sim = Sim() def _get_url(self, page, planet=None): url = self.PAGES[page] if planet is not None: url += '&cp=%s' % planet.id return url def _prepare_logger(self): self.logger = logging.getLogger("mechanize") fh = RotatingFileHandler('bot.log', maxBytes=100000, backupCount=5) sh = logging.StreamHandler() fmt = logging.Formatter(fmt='%(asctime)s %(levelname)s %(message)s', datefmt='%m-%d, %H:%M:%S') fh.setFormatter(fmt) sh.setFormatter(fmt) self.logger.setLevel(logging.INFO) self.logger.addHandler(fh) self.logger.addHandler(sh) def _prepare_browser(self): self.br = mechanize.Browser() self.br.set_handle_equiv(True) self.br.set_handle_redirect(True) self.br.set_handle_referer(True) self.br.set_handle_robots(False) self.br.addheaders = self.HEADERS def _parse_build_url(self, js): """ convert: `sendBuildRequest('url', null, 1)`; into: `url` """ return self.RE_BUILD_REQUEST.findall(js)[0] def _parse_server_time(self, content): return self.RE_SERVER_TIME.findall(content)[0] def get_mother(self): for p in self.planets: if p.mother: return p return p[0] if self.planets else None def get_closest_planet(self, p): def min_dist(p, d): return d _, d, _ = p.split(":") return sorted([(planet, planet.get_distance(p)) for planet in self.planets], key=lambda x: x[1])[0][0] def find_planet(self, name=None, coords=None, id=None, is_moon=None): if is_moon: planets = self.moons else: planets = self.planets for p in planets: if name == p.name or coords == p.coords or id == p.id: return p def get_safe_planet(self, planet): ''' Get first planet which is not under attack and isn't `planet` ''' unsafe_planets = [a.planet for a in self.active_attacks] for p in self.planets: if not p in unsafe_planets and p != planet: return p # no safe planets! go to mother return self.planets[0] def login(self, username=None, password=None): username = username or self.username password = password or self.password try: resp = self.br.open(self.MAIN_URL, timeout=10) soup = BeautifulSoup(resp) except: return False alert = soup.find(id='attack_alert') # no redirect on main page == user logged in if resp.geturl() != self.BASE_URL and alert: self.logged_in = True self.logger.info('Logged as: %s' % username) return True self.logger.info('Logging in..') self.br.select_form(name='loginForm') self.br.form['uni'] = ['s%s-pl.ogame.gameforge.com' % self.uni] self.br.form['login'] = username self.br.form['pass'] = password self.br.submit() if self.br.geturl().startswith(self.MAIN_URL): self.logged_in = True self.logger.info('Logged as: %s' % username) return True else: self.logged_in = False self.logger.error('Login failed!') return False def calc_time(self, resp): try: y, mo, d, h, mi, sec = map(int, self._parse_server_time(resp).split(',')) except: self.logger.error('Exception while calculating time') else: self.local_time = n = datetime.now() self.server_time = datetime(n.year, n.month, n.day, h, mi, sec) self.time_diff = self.server_time - self.local_time self.logger.info('Server time: %s, local time: %s' % \ (self.server_time, self.local_time)) def fetch_planets(self): self.logger.info('Fetching planets..') resp = self.br.open(self.PAGES['main']).read() self.calc_time(resp) soup = BeautifulSoup(resp) self.planets = [] self.moons = [] try: for i, c in enumerate(soup.findAll('a', 'planetlink')): name = c.find('span', 'planet-name').text coords = c.find('span', 'planet-koords').text[1:-1] url = c.get('href') p_id = int(c.parent.get('id').split('-')[1]) construct_mode = len(c.parent.findAll('a', 'constructionIcon')) != 0 p = Planet(p_id, name, coords, url, construct_mode) if i == 0: p.mother = True self.planets.append(p) #check if planet has moon moon = c.parent.find('a', 'moonlink') if moon and 'moonlink' in moon['class']: url = moon.get('href') m_id = url.split('cp=')[1] m = Moon(m_id, coords, url) self.moons.append(m) except: self.logger.exception('Exception while fetching planets') else: self.check_attacks(soup) def handle_planets(self): self.fetch_planets() for p in iter(self.planets): self.update_planet_info(p) self.update_planet_fleet(p) for m in iter(self.moons): self.update_planet_info(m) self.update_planet_fleet(m) def update_planet_fleet(self, planet): resp = self.br.open(self._get_url('fleet', planet)) soup = BeautifulSoup(resp) ships = {} for k, v in self.SHIPS.iteritems(): available = 0 try: s = soup.find(id='button' + v) available = int(s.find('span', 'textlabel').nextSibling.replace('.', '')) except: available = 0 ships[k] = available #self.logger.info('Updating %s fleet' % planet) #self.logger.info('%s' % fleet) planet.ships = ships def update_planet_info(self, planet): in_construction_mode = False resp = self.br.open(self._get_url('resources', planet)) soup = BeautifulSoup(resp) try: metal = int(soup.find(id='resources_metal').text.replace('.','')) planet.resources['metal'] = metal crystal = int(soup.find(id='resources_crystal').text.replace('.','')) planet.resources['crystal'] = crystal deuterium = int(soup.find(id='resources_deuterium').text.replace('.','')) planet.resources['deuterium'] = deuterium energy = int(soup.find(id='resources_energy').text.replace('.','')) planet.resources['energy'] = energy except: self.logger.exception('Exception while updating resources info') else: self.logger.info('Updating resources info for %s:' % planet) s = 'metal - %(metal)s, crystal - %(crystal)s, deuterium - %(deuterium)s' self.logger.info(s % planet.resources) if planet.is_moon(): return try: buildingList = soup.find(id='building') buildings = ('metalMine', 'crystalMine', 'deuteriumMine', 'solarPlant', 'fusionPlant', 'solarSatellite' ) for building, b in zip(buildings, buildingList.findAll('li')): can_build = 'on' in b.get('class') fb = b.find('a', 'fastBuild') build_url = fb.get('onclick') if fb else '' if build_url: build_url = self._parse_build_url(build_url) try: level = int(b.find('span', 'textlabel').nextSibling) except AttributeError: try: level = int(b.find('span', 'level').text) except: pass suff_energy = planet.resources['energy'] - self.sim.upgrade_energy_cost(building, level+1) > 0 res = dict( level=level, can_build=can_build, build_url=build_url, sufficient_energy=suff_energy ) planet.buildings[building] = res if buildingList.find('div', 'construction'): in_construction_mode = True except: self.logger.exception('Exception while updating buildings info') return False else: self.logger.info('%s buildings were updated' % planet) if not in_construction_mode: text, url = planet.get_mine_to_upgrade() if url: self.logger.info('Building upgrade on %s: %s'% (planet, text)) self.br.open(url) planet.in_construction_mode = True #let now transport manager to clear building queue self.transport_manager.update_building(planet) else: self.logger.info('Building queue is not empty') return True def transport_resources(self): tasks = self.transport_manager.find_dest_planet(self.planets) if tasks is None: return False self.logger.info(self.transport_manager.get_summary()) for task in iter(tasks): self.logger.info('Transport attempt from: %s, to: %s with resources %s' \ % (task['from'], task['where'], task['resources'])) result = self.send_fleet( task['from'], task['where'].coords, fleet=task['from'].get_fleet_for_resources(task['resources']), resources=task['resources'], mission='transport' ) if result: self.transport_manager.update_sent_resources(task['resources']) self.logger.info('Resources sent: %s, resources needed: %s' \ % (task['resources'], self.transport_manager.get_resources_needed())) return True def build_defense(self, planet): """ Build defense for all resources on the planet 1. plasma 2. gauss 3. heavy cannon 4. light cannon 5. rocket launcher """ url = self._get_url('defense', planet) resp = self.br.open(url) for t in ('406', '404', '403', '402', '401'): self.br.select_form(name='form') self.br.form.new_control('text','menge',{'value':'100'}) self.br.form.fixup() self.br['menge'] = '100' self.br.form.new_control('text','type',{'value':t}) self.br.form.fixup() self.br['type'] = t self.br.form.new_control('text','modus',{'value':'1'}) self.br.form.fixup() self.br['modus'] = '1' self.br.submit() def get_player_status(self, destination, origin_planet=None): if not destination: return status = {} origin_planet = origin_planet or self.get_closest_planet(destination) galaxy, system, position = destination.split(':') url = self._get_url('galaxyCnt', origin_planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) soup.find(id='galaxytable') planets = soup.findAll('tr', {'class': 'row'}) target_planet = planets[int(position)-1] name_el = target_planet.find('td', 'playername') status['name'] = name_el.find('span').text status['inactive'] = 'inactive' in name_el.get('class', '') return status def find_inactive_nearby(self, planet, radius=15): self.logger.info("Searching idlers near %s in radius %s" % (planet, radius)) nearby_systems = planet.get_nearby_systems(radius) idlers = [] for system in nearby_systems: galaxy, system = system.split(":") url = self._get_url('galaxyCnt', planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) galaxy_el = soup.find(id='galaxytable') planets = galaxy_el.findAll('tr', {'class': 'row'}) for pl in planets: name_el = pl.find('td', 'playername') debris_el = pl.find('td', 'debris') inactive = 'inactive' in name_el.get('class', '') debris_not_found = 'js_no_action' in debris_el.get('class', '') if not inactive or not debris_not_found: continue position = pl.find('td', 'position').text coords = "%s:%s:%s" % (galaxy, system, position) player_id = name_el.find('a').get('rel') player_info = soup.find(id=player_id) rank_el = player_info.find('li', 'rank') if not rank_el: continue rank = int(rank_el.find('a').text) if rank > 4000 or rank < 900: continue idlers.append(coords) time.sleep(2) return idlers def find_inactives(self): inactives = [] for p in self.planets: try: idlers = self.find_inactive_nearby(p) self.logger.info(" ".join(idlers)) inactives.extend(idlers) except Exception as e: self.logger.exception(e) continue time.sleep(5) self.logger.info(" ".join(inactives)) self.inactives = list(set(inactives)) self.logger.info(inactives) def send_fleet(self, origin_planet, destination, fleet={}, resources={}, mission='attack', target='planet', speed=None): if origin_planet.coords == destination: self.logger.error('Cannot send fleet to the same planet') return False self.logger.info('Sending fleet from %s to %s (%s)' \ % (origin_planet, destination, mission)) resp = self.br.open(self._get_url('fleet', origin_planet)) try: try: self.br.select_form(name='shipsChosen') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False soup = BeautifulSoup(resp) for ship, num in fleet.iteritems(): s = soup.find(id='button' + self.SHIPS[ship]) num = int(num) try: available = int(s.find('span', 'textlabel').nextSibling.replace('.', '')) except: available = 0 if available < num and mission in ('attack', 'expedition'): self.logger.info('No available ships to send') return False if num > 0: self.br.form['am' + self.SHIPS[ship]] = str(num) self.br.submit() try: self.br.select_form(name='details') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False galaxy, system, position = destination.split(':') self.br['galaxy'] = galaxy self.br['system'] = system self.br['position'] = position self.br.form.find_control("type").readonly = False self.br['type'] = self.TARGETS[target] self.br.form.find_control("speed").readonly = False if speed: self.br['speed'] = self.SPEEDS[speed] self.br.submit() self.br.select_form(name='sendForm') self.br.form.find_control("mission").readonly = False self.br.form['mission'] = self.MISSIONS[mission] if 'metal' in resources: self.br.form['metal'] = str(resources['metal']) if 'crystal' in resources: self.br.form['crystal'] = str(resources['crystal']) if 'deuterium' in resources: self.br.form['deuterium'] = str(resources['deuterium']) self.br.submit() except Exception as e: self.logger.exception(e) return False else: if mission == 'attack': self.farm_no += 1 return True def send_message(self, url, player, subject, message): self.logger.info('Sending message to %s: %s' % (player, message)) self.br.open(url) self.br.select_form(nr=0) self.br.form['betreff'] = subject self.br.form['text'] = message self.br.submit() def send_sms(self, msg): from smsapigateway import SMSAPIGateway try: SMSAPIGateway().send(msg) except Exception as e: self.logger.exception(str(e)) def handle_attacks(self): attack_opts = options['attack'] send_sms = bool(options['sms']['send_sms']) for a in self.active_attacks: if a.is_dangerous(): self.logger.info('Handling attack: %s' % a) if not a.planet.is_moon(): self.build_defense(a.planet) if send_sms and not a.sms_sent: self.send_sms(a.get_sms_text()) a.sms_sent = True if send_sms and not a.message_sent: self.send_message(a.message_url, a.player, attack_opts['message_topic'], a.get_random_message()) a.message_sent = True self.fleet_save(a.planet) def check_attacks(self, soup): alert = soup.find(id='attack_alert') if not alert: self.logger.exception('Check attack failed') return if 'noAttack' in alert.get('class', ''): self.logger.info('No attacks') self.active_attacks = [] else: self.logger.info('ATTACK!') resp = self.br.open(self.PAGES['events']) soup = BeautifulSoup(resp) hostile = False try: for tr in soup.findAll('tr'): countDown = tr.find('td', 'countDown') if countDown and 'hostile' in countDown.get('class', ''): hostile = True # First: check if attack was noticed if tr.get('id'): attack_id = tr.get('id').split('-')[1] elif countDown.get('id'): attack_id = countDown.get('id').split('-')[2] if not attack_id or attack_id in [a.id for a in self.active_attacks]: continue try: # Attack first discovered: save attack info arrivalTime = tr.find('td', 'arrivalTime').text.split(' ')[0] coordsOrigin = tr.find('td', 'coordsOrigin') if coordsOrigin: if coordsOrigin.find('a'): coordsOrigin = coordsOrigin.find('a').text.strip()[1:-1] destCoords = tr.find('td', 'destCoords') if destCoords: destCoords = destCoords.find('a').text.strip()[1:-1] originFleet = tr.find('td', 'originFleet') detailsFleet = int(tr.find('td', 'detailsFleet').span.text.replace('.', '')) player_info = originFleet.find('a') message_url = player_info.get('href') player = player_info.get('data-player-name') is_moon = False # TODO! planet = self.find_planet(coords=destCoords, is_moon=is_moon) a = Attack(planet, attack_id, arrivalTime, coordsOrigin, destCoords, detailsFleet, player, message_url) self.active_attacks.append(a) except Exception as e: self.logger.exception(e) self.send_sms('ATTACKEROR') if not hostile: self.active_attacks = [] except Exception as e: self.logger.exception(e) def fleet_save(self, p): if not p.has_ships(): return fleet = p.ships #recyclers are staying! #fleet['rc'] = 0 self.logger.info('Making fleet save from %s' % p) self.send_fleet(p, self.get_safe_planet(p).coords, fleet=fleet, mission='station', speed=10, resources={'metal': p.resources['metal']+500, 'crystal': p.resources['crystal']+500, 'deuterium': p.resources['deuterium']+500}) def collect_debris(self, p): if not p.has_ships(): return self.logger.info('Collecting debris from %s using %s recyclers' % (p, p.ships['rc'])) self.send_fleet(p, p.coords, fleet={'rc':p.ships['rc']}, mission='collect', target='debris') def send_expedition(self): expedition = options['expedition'] planets = expedition['planets'].split(' ') random.shuffle(planets) for coords in planets[:3]: planet = self.find_planet(coords=coords) if planet: galaxy, system, position = planet.coords.split(':') expedition_coords = '%s:%s:16' % (galaxy, system) self.send_fleet(planet, expedition_coords, fleet={expedition['ships_kind']:expedition['ships_number']}, mission='expedition') def farm(self): farms = options['farming']['farms'].split(' ') ships_kind = options['farming']['ships_kind'] ships_number = options['farming']['ships_number'] l = len(farms) if l == 0 or not farms[0]: return farm = farms[self.farm_no%l] if not self.get_player_status(farm)['inactive']: self.farm_no += 1 self.logger.error('farm %s seems not to be inactive!', farm) return self.send_fleet( self.get_closest_planet(farm), farm, fleet={ships_kind:ships_number} ) def sleep(self): sleep_options = options['general'] sleep_time = randint(0, int(sleep_options['seed']))+int(sleep_options['check_interval']) self.logger.info('Sleeping for %s secs' % sleep_time) if self.active_attacks: sleep_time = 60 time.sleep(sleep_time) def stop(self): self.logger.info('Stopping bot') os.unlink(self.pidfile) def start(self): self.logger.info('Starting bot') self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) #main loop while True: if self.login(): try: self.handle_planets() #self.find_inactives() if not self.active_attacks: if True or not self.transport_resources(): self.send_expedition() self.farm() self.farm() else: self.handle_attacks() except Exception as e: self.logger.exception(e) #self.stop() #return else: self.logger.error('Login failed!') #self.stop() #return self.sleep()
class Bot(object): BASE_URL = 'http://labdcc.fceia.unr.edu.ar/~jgalat/' LOGIN_URL = 'http://labdcc.fceia.unr.edu.ar/~jgalat/' HEADERS = [('User-agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64)\ AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15')] RE_BUILD_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\', null, 1\)") RE_SERVER_TIME = re.compile(r"<th>Server time </th>\s*<th.*>(.*)</th>") RE_RESOURCE = re.compile(r"(?:<font >)?(\d+)(?:/\d*)?(?:</font>)?") RE_BUILD_LVL = re.compile(r"(?: \(level (\d*)\))|<br />") RE_IN_CONSTRUCTION = re.compile(r"\d+.: (.*) (\d+)") RE_SHIP_COST = re.compile( r"(\w*): <b style=\"color:\w*;\">(?: <t title=\"-(?:\d|\.)+\"><span class=\"noresources\">)?((?:\d|\.)+)" ) # ship -> ship id on the page SHIPS = { 'lm': '204', 'hm': '205', 'cr': '206', 'ow': '207', 'pn': '215', 'bb': '211', 'ns': '213', 'gs': '214', 'lt': '202', 'dt': '203', 'cs': '208', 'rc': '209', 'ss': '210' } # mission ids MISSIONS = { 'Attack': '1', 'Transport': '3', 'Hold Position': '5', 'Expedition': '15', 'Collect': '8' } TARGETS = {'Planet': '1', 'Moon': '3', 'Debris': '2'} def __init__(self, username, password, server): self.username = username self.password = password self.logged_in = False self._prepare_logger() self._prepare_browser() farms = options['farming']['farms'] self.farm_no = randint(0, len(farms) - 1) if farms else 0 self.MAIN_URL = 'http://labdcc.fceia.unr.edu.ar/~jgalat/game.php' server = server.replace('http://', '') if server[-1] == '/': server = server[:-1] self.MAIN_URL = 'http://' + server + '/game.php' self.PAGES = { 'main': self.MAIN_URL + '?page=overview', 'buildings': self.MAIN_URL + '?page=buildings', 'station': self.MAIN_URL + '?page=station', 'research': self.MAIN_URL + '?page=buildings&mode=research', 'shipyard': self.MAIN_URL + '?page=buildings&mode=fleet', 'defense': self.MAIN_URL + '?page=defense', 'fleet': self.MAIN_URL + '?page=fleet', 'galaxy': self.MAIN_URL + '?page=galaxy', 'galaxyCnt': self.MAIN_URL + '?page=galaxyContent', 'events': self.MAIN_URL + '?page=eventList', } self.planets = [] self.moons = [] self.active_attacks = [] self.fleet_slots = 0 self.active_fleets = 0 self.server_time = self.local_time = datetime.now() self.time_diff = 0 self.emergency_sms_sent = False self.transport_manager = TransportManager() self.sim = Sim() def _get_url(self, page, planet=None): url = self.PAGES[page] if planet is not None: url += '&cp=%s' % planet.id return url def _prepare_logger(self): self.logger = logging.getLogger("mechanize") fh = RotatingFileHandler('bot.log', maxBytes=100000, backupCount=5) sh = logging.StreamHandler() fmt = logging.Formatter(fmt='%(asctime)s %(levelname)s %(message)s', datefmt='%m-%d, %H:%M:%S') fh.setFormatter(fmt) sh.setFormatter(fmt) self.logger.setLevel(logging.INFO) self.logger.addHandler(fh) self.logger.addHandler(sh) self.logger.propagate = False def _prepare_browser(self): self.br = mechanize.Browser() self.br.set_handle_equiv(True) self.br.set_handle_redirect(True) self.br.set_handle_referer(True) self.br.set_handle_robots(False) self.br.addheaders = self.HEADERS def _parse_build_url(self, js): """ convert: `sendBuildRequest('url', null, 1)`; into: `url` """ return self.RE_BUILD_REQUEST.findall(js)[0] def _parse_server_time(self, content): return self.RE_SERVER_TIME.findall(content)[0] def get_mother(self): for p in self.planets: if p.mother: return p return p[0] if self.planets else None def get_closest_planet(self, p): def min_dist(p, d): return d _, d, _ = p.split(":") return sorted([(planet, planet.get_distance(p)) for planet in self.planets], key=lambda x: x[1])[0][0] def find_planet(self, name=None, coords=None, id=None, is_moon=None): if is_moon: planets = self.moons else: planets = self.planets for p in planets: if name == p.name or coords == p.coords or id == p.id: return p def get_safe_planet(self, planet): ''' Get first planet which is not under attack and isn't `planet` ''' unsafe_planets = [a.planet for a in self.active_attacks] for p in self.planets: if not p in unsafe_planets and p != planet: return p # no safe planets! go to mother return self.planets[0] def login(self, username=None, password=None): username = username or self.username password = password or self.password try: resp = self.br.open(self.MAIN_URL, timeout=10) soup = BeautifulSoup(resp) except: return False # no redirect on main page == user logged in if resp.geturl() == self.MAIN_URL: self.logged_in = True self.logger.info('Logged as: %s' % username) return True self.logger.info('Logging in..') self.br.select_form(nr=0) self.br.form['username'] = username self.br.form['password'] = password self.br.submit() if self.br.geturl().startswith(self.MAIN_URL): self.logged_in = True self.logger.info('Logged as: %s' % username) return True else: self.logged_in = False self.logger.error('Login failed!') return False def calc_time(self, resp): try: self.server_time = datetime.strptime( str(date.today().year) + ' ' + self._parse_server_time(resp), "%Y %a %b %d %H:%M:%S") except: self.logger.error('Exception while calculating time') else: self.local_time = n = datetime.now() self.time_diff = self.server_time - self.local_time self.logger.info('Server time: %s, local time: %s' % (self.server_time, self.local_time)) def fetch_planets(self): self.logger.info('Fetching planets..') resp = self.br.open(self.PAGES['main']).read() self.calc_time(resp) soup = BeautifulSoup(resp) self.planets = [] self.moons = [] try: for i, c in enumerate(soup.find('select').findAll('option')): url = c['value'] name, coords = c.contents[0].split(' ')[0:2] p = Planet('1', name, coords[1:-1], url, False) if i == 0: p.mother = True self.planets.append(p) #p_id = int(c.parent.get('id').split('-')[1]) # construct_mode = len( # c.parent.findAll( # 'a', # 'constructionIcon')) != 0 # check if planet has moon # moon = c.parent.find('a', 'moonlink') # if moon and 'moonlink' in moon['class']: # url = moon.get('href') # m_id = url.split('cp=')[1] # m = Moon(m_id, coords, url) # self.moons.append(m) except: self.logger.exception('Exception while fetching planets') # else: # self.check_attacks(soup) def handle_planets(self): for p in iter(self.planets): self.upgrade_planet(p) self.farm() def load_planets(self): self.fetch_planets() for p in iter(self.planets): self.update_planet_shipyard(p) self.update_planet_info(p) self.update_planet_research(p) self.update_planet_fleet(p) for m in iter(self.moons): self.update_planet_info(m) self.update_planet_fleet(m) def upgrade_planet(self, planet): upds = planet.get_upgrades() if 'buildings' in upds: for name in upds['buildings']: self.logger.info('Building upgrade on %s: %s' % (planet, name)) self.br.open(planet.buildings[name]['link']) # let now transport manager to clear building queue # self.transport_manager.update_building(planet) if 'researches' in upds: for name in upds['researches']: self.logger.info('Building research on %s: %s' % (planet, name)) self.br.open(planet.researches[name]['link']) if 'fleets' in upds: resp = self.br.open(self._get_url('shipyard', planet)) soup = BeautifulSoup(resp) planet.ships = {} formdata = {} for c in soup.findAll('td', {'class': 'l'}): name = c.find('a').contents[0] intxt = c.findNext('th') if not intxt: continue intxt = intxt.find('input') if not intxt: continue if name in upds['fleets']: formdata[intxt['name']] = str(upds['fleets'][name]) self.logger.info('Building %d %s on %s' % (upds['fleets'][name], name, planet)) else: formdata[intxt['name']] = "0" self.br.select_form(nr=0) for name, value in formdata.iteritems(): self.br.form[name] = value self.br.submit() if not upds: self.logger.info('Nothing to upgrade or build on %s' % planet) return True def update_planet_fleet(self, planet): planet.ships = {} try: resp = self.br.open(self._get_url('fleet', planet)) soup = BeautifulSoup(resp) for c in soup.find('form', { 'action': 'game.php?page=fleet1' }).findAll('tr')[2:-2]: name = c.find('a').contents[0].strip() cant = int(c.find('th').findNext('th').contents[0].strip()) planet.ships[name] = cant except: self.logger.exception('Exception while updating fleets info') s = ', '.join(["%s: %d" % (n, c) for n, c in planet.ships.iteritems()]) self.logger.info('Ships on %s-> %s' % (planet, s)) return True def update_planet_shipyard(self, planet): resp = self.br.open(self._get_url('shipyard', planet)) soup = BeautifulSoup(resp) planet.buyable_fleets = {} for c in soup.findAll('td', {'class': 'l'}): name = c.find('a').contents[0] intxt = c.findNext('th') if not intxt: continue res = self.RE_SHIP_COST.findall("".join(map(str, c.contents))) res = {x: int(y.replace('.', '')) for x, y in res} planet.buyable_fleets[name] = res return True def update_planet_info(self, planet): resp = self.br.open(self._get_url('buildings', planet)) soup = BeautifulSoup(resp) names = ['Metal', 'Crystal', 'Deuterium', 'Energy'] try: for name, c in zip(names, soup.find(id='resources').findAll(width='90')): matched = self.RE_RESOURCE.findall( str(c.contents[0]).replace('.', ''))[0] planet.resources[name] = int(matched) except: self.logger.exception('Exception while updating resources info') else: self.logger.info('Resources in %s:' % planet) s = 'Metal - %(Metal)s, Crystal - %(Crystal)s, Deuterium - %(Deuterium)s' self.logger.info(s % planet.resources) if planet.is_moon(): return try: planet.buildings = {} for c in soup.find('table', { 'width': '530' }).findAll('td', {'class': 'l'}): if len(c.attrs) == 1: childs = c.findChildren() if len(childs) > 2: name = c.find('a').contents[0] exp = self.RE_BUILD_LVL.findall(str(c.contents[2])) if not exp or not exp[0]: lvl = 0 else: lvl = int(exp[0]) suff_energy = planet.resources[ 'energy'] - self.sim.upgrade_energy_cost( name, lvl + 1) > 0 canbuild = c.find('b', {'style': "color:red;"}) is None c2 = c.findNext('td').find('a') if c2 and canbuild: link = c2['href'] else: link = None planet.buildings[name] = { 'link': link, 'level': lvl, 'sufficient_energy': suff_energy, 'in_construction': False } for c in soup.find('table', { 'width': '530' }).findAll('td', {'class': 'l'}): if len(c.attrs) > 1: name, lvl = self.RE_IN_CONSTRUCTION.findall( str(c.contents[0]))[0] planet.buildings[name]['link'] = None planet.buildings[name]['level'] = max( planet.buildings[name]['level'], int(lvl)) planet.buildings[name]['in_construction'] = True except: self.logger.exception('Exception while reloading buildings info') return False else: self.logger.info('%s buildings were reloaded' % planet) return True def update_planet_research(self, planet): resp = self.br.open(self._get_url('research', planet)) soup = BeautifulSoup(resp) if planet.is_moon(): return try: planet.researches = {} tabla = soup.find('table', {'width': '530'}) if tabla is None: return True for c in tabla.findAll('td', {'class': 'l'}): if len(c.attrs) == 1: childs = c.findChildren() if len(childs) > 2: name = c.find('a').contents[0] exp = self.RE_BUILD_LVL.findall(str(c.contents[2])) if not exp or not exp[0]: lvl = 0 else: lvl = int(exp[0]) canbuild = c.find('b', {'style': "color:red;"}) is None c2 = c.findNext('th', {'class': 'l'}).find('a') if c2 and canbuild: link = c2['href'] else: link = None planet.researches[name] = { 'link': link, 'level': lvl, 'in_construction': False } except: self.logger.exception('Exception while reloading researches info') return False else: self.logger.info('%s researches were reloaded' % planet) return True def transport_resources(self): tasks = self.transport_manager.find_dest_planet(self.planets) if tasks is None: return False self.logger.info(self.transport_manager.get_summary()) for task in iter(tasks): self.logger.info( 'Transport attempt from: %s, to: %s with resources %s' % (task['from'], task['where'], task['resources'])) result = self.send_fleet( task['from'], task['where'].coords, fleet=task['from'].get_fleet_for_resources(task['resources']), resources=task['resources'], mission='transport') if result: self.transport_manager.update_sent_resources(task['resources']) self.logger.info( 'Resources sent: %s, resources needed: %s' % (task['resources'], self.transport_manager.get_resources_needed())) return True def build_defense(self, planet): """ Build defense for all resources on the planet 1. plasma 2. gauss 3. heavy cannon 4. light cannon 5. rocket launcher """ url = self._get_url('defense', planet) resp = self.br.open(url) for t in ('406', '404', '403', '402', '401'): self.br.select_form(name='form') self.br.form.new_control('text', 'menge', {'value': '100'}) self.br.form.fixup() self.br['menge'] = '100' self.br.form.new_control('text', 'type', {'value': t}) self.br.form.fixup() self.br['type'] = t self.br.form.new_control('text', 'modus', {'value': '1'}) self.br.form.fixup() self.br['modus'] = '1' self.br.submit() def get_player_status(self, destination, origin_planet=None): if not destination: return status = {} origin_planet = origin_planet or self.get_closest_planet(destination) galaxy, system, position = destination.split(':') url = self._get_url('galaxyCnt', origin_planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) soup.find(id='galaxytable') planets = soup.findAll('tr', {'class': 'row'}) target_planet = planets[int(position) - 1] name_el = target_planet.find('td', 'playername') status['name'] = name_el.find('span').text status['inactive'] = 'inactive' in name_el.get('class', '') return status def find_inactive_nearby(self, planet, radius=15): self.logger.info("Searching idlers near %s in radius %s" % (planet, radius)) nearby_systems = planet.get_nearby_systems(radius) idlers = [] for system in nearby_systems: galaxy, system = system.split(":") url = self._get_url('galaxyCnt', planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) galaxy_el = soup.find(id='galaxytable') planets = galaxy_el.findAll('tr', {'class': 'row'}) for pl in planets: name_el = pl.find('td', 'playername') debris_el = pl.find('td', 'debris') inactive = 'inactive' in name_el.get('class', '') debris_not_found = 'js_no_action' in debris_el.get('class', '') if not inactive or not debris_not_found: continue position = pl.find('td', 'position').text coords = "%s:%s:%s" % (galaxy, system, position) player_id = name_el.find('a').get('rel') player_info = soup.find(id=player_id) rank_el = player_info.find('li', 'rank') if not rank_el: continue rank = int(rank_el.find('a').text) if rank > 4000 or rank < 900: continue idlers.append(coords) time.sleep(2) return idlers def find_inactives(self): inactives = [] for p in self.planets: try: idlers = self.find_inactive_nearby(p) self.logger.info(" ".join(idlers)) inactives.extend(idlers) except Exception as e: self.logger.exception(e) continue time.sleep(5) self.logger.info(" ".join(inactives)) self.inactives = list(set(inactives)) self.logger.info(inactives) def send_fleet(self, origin_planet, destination, fleet={}, resources={}, mission='Attack', target='Planet', speed=None, holdingtime=None): if origin_planet.coords == destination: self.logger.error('Cannot send fleet to the same planet') return False self.logger.info('Sending fleet from %s to %s (%s)' % (origin_planet, destination, mission)) try: resp = self.br.open(self._get_url('fleet', origin_planet)) try: self.br.select_form( predicate=lambda f: 'action' in f.attrs and f.attrs[ 'action'] == 'game.php?page=fleet1') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False #resp=open("samples/fleet.html", "r").read() soup = BeautifulSoup(resp) sended = set() for c in soup.find('form', { 'action': 'game.php?page=fleet1' }).findAll('tr')[2:-2]: name = c.find('a').contents[0].strip() if name in fleet: inp = c.find('input')['name'].strip() sended.add(name) self.br.form[inp] = str(fleet[name]) for name in fleet.iterkeys(): if name not in sended: self.logger.info("Couldn't send all ships to mission") return False self.br.submit() try: self.br.select_form( predicate=lambda f: 'action' in f.attrs and f.attrs[ 'action'] == 'game.php?page=fleet2') except mechanize.FormNotFoundError: self.logger.info('Error while sending ships, fleet2') return False galaxy, system, position = destination.split(':') self.br.form['galaxy'] = galaxy self.br.form['system'] = system self.br.form['planet'] = position self.br.form['planettype'] = [self.TARGETS[target]] if speed: self.br.form['speed'][0] = str(floor(int(speed) / 10)) self.br.submit() try: self.br.select_form( predicate=lambda f: 'action' in f.attrs and f.attrs[ 'action'] == 'game.php?page=fleet3') except mechanize.FormNotFoundError: self.logger.info('Error while sendind ships, fleet 3') return False self.br.form['mission'] = [self.MISSIONS[mission]] if holdingtime: self.br.form['holdingtime'] = [str(holdingtime)] if 'Metal' in resources: self.br.form['resource1'] = str(resources['Metal']) if 'Crystal' in resources: self.br.form['resource2'] = str(resources['Crystal']) if 'Deuterium' in resources: self.br.form['resource3'] = str(resources['Deuterium']) self.br.submit() last_response = self.br.response( ) # This is returned by br.open(...) too print last_response.geturl() print last_response.info() except Exception as e: self.logger.exception(e) return False return True def send_message(self, url, player, subject, message): self.logger.info('Sending message to %s: %s' % (player, message)) self.br.open(url) self.br.select_form(nr=0) self.br.form['betreff'] = subject self.br.form['text'] = message self.br.submit() def send_sms(self, msg): from smsapigateway import SMSAPIGateway try: SMSAPIGateway().send(msg) except Exception as e: self.logger.exception(str(e)) def handle_attacks(self): attack_opts = options['attack'] send_sms = bool(options['sms']['send_sms']) for a in self.active_attacks: if a.is_dangerous(): self.logger.info('Handling attack: %s' % a) if not a.planet.is_moon(): self.build_defense(a.planet) if send_sms and not a.sms_sent: self.send_sms(a.get_sms_text()) a.sms_sent = True if send_sms and not a.message_sent: self.send_message(a.message_url, a.player, attack_opts['message_topic'], a.get_random_message()) a.message_sent = True self.fleet_save(a.planet) def check_attacks(self, soup): alert = soup.find(id='attack_alert') if not alert: self.logger.exception('Check attack failed') return if 'noAttack' in alert.get('class', ''): self.logger.info('No attacks') self.active_attacks = [] else: self.logger.info('ATTACK!') resp = self.br.open(self.PAGES['events']) soup = BeautifulSoup(resp) hostile = False try: for tr in soup.findAll('tr'): countDown = tr.find('td', 'countDown') if countDown and 'hostile' in countDown.get('class', ''): hostile = True # First: check if attack was noticed if tr.get('id'): attack_id = tr.get('id').split('-')[1] elif countDown.get('id'): attack_id = countDown.get('id').split('-')[2] if not attack_id or attack_id in [ a.id for a in self.active_attacks ]: continue try: # Attack first discovered: save attack info arrivalTime = tr.find( 'td', 'arrivalTime').text.split(' ')[0] coordsOrigin = tr.find('td', 'coordsOrigin') if coordsOrigin: if coordsOrigin.find('a'): coordsOrigin = coordsOrigin.find( 'a').text.strip()[1:-1] destCoords = tr.find('td', 'destCoords') if destCoords: destCoords = destCoords.find( 'a').text.strip()[1:-1] originFleet = tr.find('td', 'originFleet') detailsFleet = int( tr.find('td', 'detailsFleet').span.text.replace( '.', '')) player_info = originFleet.find('a') message_url = player_info.get('href') player = player_info.get('data-player-name') is_moon = False # TODO! planet = self.find_planet(coords=destCoords, is_moon=is_moon) a = Attack(planet, attack_id, arrivalTime, coordsOrigin, destCoords, detailsFleet, player, message_url) self.active_attacks.append(a) except Exception as e: self.logger.exception(e) self.send_sms('ATTACKEROR') if not hostile: self.active_attacks = [] except Exception as e: self.logger.exception(e) def fleet_save(self, p): if not p.has_ships(): return fleet = p.ships # recyclers are staying! #fleet['rc'] = 0 self.logger.info('Making fleet save from %s' % p) self.send_fleet(p, self.get_safe_planet(p).coords, fleet=fleet, mission='Hold Position', speed=10, resources={ 'metal': p.resources['metal'] + 500, 'crystal': p.resources['crystal'] + 500, 'deuterium': p.resources['deuterium'] + 500 }) def collect_debris(self, p): if not p.has_ships(): return self.logger.info('Collecting debris from %s using %s recyclers' % (p, p.ships['rc'])) self.send_fleet(p, p.coords, fleet={'rc': p.ships['rc']}, mission='collect', target='debris') def send_expedition(self): expedition = options['expedition'] planets = expedition['planets'].split(' ') random.shuffle(planets) for coords in planets[:3]: planet = self.find_planet(coords=coords) if planet: galaxy, system, position = planet.coords.split(':') expedition_coords = '%s:%s:16' % (galaxy, system) self.send_fleet(planet, expedition_coords, fleet={ expedition['ships_kind']: expedition['ships_number'] }, mission='expedition') def farm(self): if options['farming'].has_key('enabled') and not strtobool( options['farming']['enabled']): return farms = [ s.strip().split(' ')[0] for s in options['farming']['farms'].split(',') ] if not farms or not farms[0]: return ships_kind = options['farming']['ships_kind'] ships_number = options['farming']['ships_number'] next_farm = int(options['farming']['next_farm']) % len(farms) farm = farms[next_farm] #if not self.get_player_status(farm)['inactive']: # self.logger.error('farm %s seems not to be inactive!', farm) # return if self.send_fleet(self.get_closest_planet(farm), farm, fleet={ships_kind: ships_number}): next_farm = (next_farm + 1) % len(farms) options.change_item('farming', 'next_farm', str(next_farm)) def sleep(self): sleep_options = options['general'] sleep_time = randint(0, int(sleep_options['seed'])) + int( sleep_options['check_interval']) self.logger.info('Sleeping for %s secs' % sleep_time) if self.active_attacks: sleep_time = 60 time.sleep(sleep_time) def stop(self): self.logger.info('Stopping bot') os.unlink(self.pidfile) def interactive(self): self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) if not self.login(): self.logger.error('Login failed!') self.load_planets() def start(self): self.logger.info('Starting bot') self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) # main loop while True: if self.login(): try: self.load_planets() self.handle_planets() # self.find_inactives() # if not self.active_attacks: # if True or not self.transport_resources(): # self.send_expedition() # self.farm() # self.farm() # else: # self.handle_attacks() except Exception as e: self.logger.exception(e) # self.stop() # return else: self.logger.error('Login failed!') # self.stop() # return self.sleep()
class Bot(object): BASE_URL = 'http://labdcc.fceia.unr.edu.ar/~jgalat/' LOGIN_URL = 'http://labdcc.fceia.unr.edu.ar/~jgalat/' HEADERS = [('User-agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64)\ AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15')] RE_BUILD_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\', null, 1\)") RE_SERVER_TIME = re.compile(r"<th>Server time </th>\s*<th.*>(.*)</th>") RE_RESOURCE = re.compile(r"(?:<font >)?(\d+)(?:/\d*)?(?:</font>)?") RE_BUILD_LVL = re.compile(r"(?: \(level (\d*)\))|<br />") RE_IN_CONSTRUCTION = re.compile(r"\d+.: (.*) (\d+)") RE_SHIP_COST = re.compile( r"(\w*): <b style=\"color:\w*;\">(?: <t title=\"-(?:\d|\.)+\"><span class=\"noresources\">)?((?:\d|\.)+)") # ship -> ship id on the page SHIPS = { 'lm': '204', 'hm': '205', 'cr': '206', 'ow': '207', 'pn': '215', 'bb': '211', 'ns': '213', 'gs': '214', 'lt': '202', 'dt': '203', 'cs': '208', 'rc': '209', 'ss': '210' } # mission ids MISSIONS = { 'Attack': '1', 'Transport': '3', 'Hold Position': '5', 'Expedition': '15', 'Collect': '8' } TARGETS = { 'Planet': '1', 'Moon': '3', 'Debris': '2' } def __init__(self, username, password, server): self.username = username self.password = password self.logged_in = False self._prepare_logger() self._prepare_browser() farms = options['farming']['farms'] self.farm_no = randint(0, len(farms)-1) if farms else 0 self.MAIN_URL = 'http://labdcc.fceia.unr.edu.ar/~jgalat/game.php' server=server.replace('http://','') if server[-1]=='/': server=server[:-1] self.MAIN_URL = 'http://'+server+'/game.php' self.PAGES = { 'main': self.MAIN_URL + '?page=overview', 'buildings': self.MAIN_URL + '?page=buildings', 'station': self.MAIN_URL + '?page=station', 'research': self.MAIN_URL + '?page=buildings&mode=research', 'shipyard': self.MAIN_URL + '?page=buildings&mode=fleet', 'defense': self.MAIN_URL + '?page=defense', 'fleet': self.MAIN_URL + '?page=fleet', 'galaxy': self.MAIN_URL + '?page=galaxy', 'galaxyCnt': self.MAIN_URL + '?page=galaxyContent', 'events': self.MAIN_URL + '?page=eventList', } self.planets = [] self.moons = [] self.active_attacks = [] self.fleet_slots = 0 self.active_fleets = 0 self.server_time = self.local_time = datetime.now() self.time_diff = 0 self.emergency_sms_sent = False self.transport_manager = TransportManager() self.sim = Sim() def _get_url(self, page, planet=None): url = self.PAGES[page] if planet is not None: url += '&cp=%s' % planet.id return url def _prepare_logger(self): self.logger = logging.getLogger("mechanize") fh = RotatingFileHandler('bot.log', maxBytes=100000, backupCount=5) sh = logging.StreamHandler() fmt = logging.Formatter(fmt='%(asctime)s %(levelname)s %(message)s', datefmt='%m-%d, %H:%M:%S') fh.setFormatter(fmt) sh.setFormatter(fmt) self.logger.setLevel(logging.INFO) self.logger.addHandler(fh) self.logger.addHandler(sh) self.logger.propagate = False def _prepare_browser(self): self.br = mechanize.Browser() self.br.set_handle_equiv(True) self.br.set_handle_redirect(True) self.br.set_handle_referer(True) self.br.set_handle_robots(False) self.br.addheaders = self.HEADERS def _parse_build_url(self, js): """ convert: `sendBuildRequest('url', null, 1)`; into: `url` """ return self.RE_BUILD_REQUEST.findall(js)[0] def _parse_server_time(self, content): return self.RE_SERVER_TIME.findall(content)[0] def get_mother(self): for p in self.planets: if p.mother: return p return p[0] if self.planets else None def get_closest_planet(self, p): def min_dist(p, d): return d _, d, _ = p.split(":") return sorted([(planet, planet.get_distance(p)) for planet in self.planets], key=lambda x: x[1])[0][0] def find_planet(self, name=None, coords=None, id=None, is_moon=None): if is_moon: planets = self.moons else: planets = self.planets for p in planets: if name == p.name or coords == p.coords or id == p.id: return p def get_safe_planet(self, planet): ''' Get first planet which is not under attack and isn't `planet` ''' unsafe_planets = [a.planet for a in self.active_attacks] for p in self.planets: if not p in unsafe_planets and p != planet: return p # no safe planets! go to mother return self.planets[0] def login(self, username=None, password=None): username = username or self.username password = password or self.password try: resp = self.br.open(self.MAIN_URL, timeout=10) soup = BeautifulSoup(resp) except: return False # no redirect on main page == user logged in if resp.geturl() == self.MAIN_URL: self.logged_in = True self.logger.info('Logged as: %s' % username) return True self.logger.info('Logging in..') self.br.select_form(nr=0) self.br.form['username'] = username self.br.form['password'] = password self.br.submit() if self.br.geturl().startswith(self.MAIN_URL): self.logged_in = True self.logger.info('Logged as: %s' % username) return True else: self.logged_in = False self.logger.error('Login failed!') return False def calc_time(self, resp): try: self.server_time = datetime.strptime( str(date.today().year) + ' ' + self._parse_server_time(resp), "%Y %a %b %d %H:%M:%S") except: self.logger.error('Exception while calculating time') else: self.local_time = n = datetime.now() self.time_diff = self.server_time - self.local_time self.logger.info('Server time: %s, local time: %s' % (self.server_time, self.local_time)) def fetch_planets(self): self.logger.info('Fetching planets..') resp = self.br.open(self.PAGES['main']).read() self.calc_time(resp) soup = BeautifulSoup(resp) self.planets = [] self.moons = [] try: for i, c in enumerate(soup.find('select').findAll('option')): url = c['value'] name, coords = c.contents[0].split(' ')[0:2] p = Planet('1', name, coords[1:-1], url, False) if i == 0: p.mother = True self.planets.append(p) #p_id = int(c.parent.get('id').split('-')[1]) # construct_mode = len( # c.parent.findAll( # 'a', # 'constructionIcon')) != 0 # check if planet has moon # moon = c.parent.find('a', 'moonlink') # if moon and 'moonlink' in moon['class']: # url = moon.get('href') # m_id = url.split('cp=')[1] # m = Moon(m_id, coords, url) # self.moons.append(m) except: self.logger.exception('Exception while fetching planets') # else: # self.check_attacks(soup) def handle_planets(self): for p in iter(self.planets): self.upgrade_planet(p) self.farm() def load_planets(self): self.fetch_planets() for p in iter(self.planets): self.update_planet_shipyard(p) self.update_planet_info(p) self.update_planet_research(p) self.update_planet_fleet(p) for m in iter(self.moons): self.update_planet_info(m) self.update_planet_fleet(m) def upgrade_planet(self, planet): upds = planet.get_upgrades() if 'buildings' in upds: for name in upds['buildings']: self.logger.info('Building upgrade on %s: %s' % (planet, name)) self.br.open(planet.buildings[name]['link']) # let now transport manager to clear building queue # self.transport_manager.update_building(planet) if 'researches' in upds: for name in upds['researches']: self.logger.info('Building research on %s: %s' % (planet, name)) self.br.open(planet.researches[name]['link']) if 'fleets' in upds: resp = self.br.open(self._get_url('shipyard', planet)) soup = BeautifulSoup(resp) planet.ships = {} formdata = {} for c in soup.findAll('td', {'class': 'l'}): name = c.find('a').contents[0] intxt = c.findNext('th') if not intxt: continue intxt = intxt.find('input') if not intxt: continue if name in upds['fleets']: formdata[intxt['name']] = str(upds['fleets'][name]) self.logger.info( 'Building %d %s on %s' % (upds['fleets'][name], name, planet)) else: formdata[intxt['name']] = "0" self.br.select_form(nr=0) for name, value in formdata.iteritems(): self.br.form[name] = value self.br.submit() if not upds: self.logger.info('Nothing to upgrade or build on %s' % planet) return True def update_planet_fleet(self, planet): planet.ships = {} try: resp = self.br.open(self._get_url('fleet', planet)) soup = BeautifulSoup(resp) for c in soup.find('form', {'action': 'game.php?page=fleet1'}).findAll('tr')[2:-2]: name = c.find('a').contents[0].strip() cant = int(c.find('th').findNext('th').contents[0].strip()) planet.ships[name] = cant except: self.logger.exception('Exception while updating fleets info') s = ', '.join(["%s: %d" % (n, c) for n, c in planet.ships.iteritems()]) self.logger.info('Ships on %s-> %s' % (planet, s)) return True def update_planet_shipyard(self, planet): resp = self.br.open(self._get_url('shipyard', planet)) soup = BeautifulSoup(resp) planet.buyable_fleets = {} for c in soup.findAll('td', {'class': 'l'}): name = c.find('a').contents[0] intxt = c.findNext('th') if not intxt: continue res = self.RE_SHIP_COST.findall("".join(map(str, c.contents))) res = {x: int(y.replace('.', '')) for x, y in res} planet.buyable_fleets[name] = res return True def update_planet_info(self, planet): resp = self.br.open(self._get_url('buildings', planet)) soup = BeautifulSoup(resp) names = ['Metal', 'Crystal', 'Deuterium', 'Energy'] try: for name, c in zip(names, soup.find(id='resources').findAll(width='90')): matched = self.RE_RESOURCE.findall( str(c.contents[0]).replace('.', ''))[0] planet.resources[name] = int(matched) except: self.logger.exception('Exception while updating resources info') else: self.logger.info('Resources in %s:' % planet) s = 'Metal - %(Metal)s, Crystal - %(Crystal)s, Deuterium - %(Deuterium)s' self.logger.info(s % planet.resources) if planet.is_moon(): return try: planet.buildings = {} for c in soup.find('table', {'width': '530'}).findAll('td', {'class': 'l'}): if len(c.attrs) == 1: childs = c.findChildren() if len(childs) > 2: name = c.find('a').contents[0] exp = self.RE_BUILD_LVL.findall(str(c.contents[2])) if not exp or not exp[0]: lvl = 0 else: lvl = int(exp[0]) suff_energy = planet.resources[ 'energy'] - self.sim.upgrade_energy_cost(name, lvl+1) > 0 canbuild = c.find('b', {'style': "color:red;"}) is None c2 = c.findNext('td').find('a') if c2 and canbuild: link = c2['href'] else: link = None planet.buildings[name] = { 'link': link, 'level': lvl, 'sufficient_energy': suff_energy, 'in_construction': False} for c in soup.find('table', {'width': '530'}).findAll('td', {'class': 'l'}): if len(c.attrs) > 1: name, lvl = self.RE_IN_CONSTRUCTION.findall( str(c.contents[0]))[0] planet.buildings[name]['link']=None planet.buildings[name]['level'] = max( planet.buildings[name]['level'], int(lvl)) planet.buildings[name]['in_construction'] = True except: self.logger.exception('Exception while reloading buildings info') return False else: self.logger.info('%s buildings were reloaded' % planet) return True def update_planet_research(self, planet): resp = self.br.open(self._get_url('research', planet)) soup = BeautifulSoup(resp) if planet.is_moon(): return try: planet.researches = {} tabla=soup.find('table', {'width': '530'}) if tabla is None: return True for c in tabla.findAll('td', {'class': 'l'}): if len(c.attrs) == 1: childs = c.findChildren() if len(childs) > 2: name = c.find('a').contents[0] exp = self.RE_BUILD_LVL.findall(str(c.contents[2])) if not exp or not exp[0]: lvl = 0 else: lvl = int(exp[0]) canbuild = c.find('b', {'style': "color:red;"}) is None c2 = c.findNext('th', {'class': 'l'}).find('a') if c2 and canbuild: link = c2['href'] else: link = None planet.researches[name] = { 'link': link, 'level': lvl, 'in_construction': False} except: self.logger.exception('Exception while reloading researches info') return False else: self.logger.info('%s researches were reloaded' % planet) return True def transport_resources(self): tasks = self.transport_manager.find_dest_planet(self.planets) if tasks is None: return False self.logger.info(self.transport_manager.get_summary()) for task in iter(tasks): self.logger.info( 'Transport attempt from: %s, to: %s with resources %s' % (task['from'], task['where'], task['resources'])) result = self.send_fleet( task['from'], task['where'].coords, fleet=task['from'].get_fleet_for_resources(task['resources']), resources=task['resources'], mission='transport' ) if result: self.transport_manager.update_sent_resources(task['resources']) self.logger.info( 'Resources sent: %s, resources needed: %s' % (task['resources'], self.transport_manager.get_resources_needed())) return True def build_defense(self, planet): """ Build defense for all resources on the planet 1. plasma 2. gauss 3. heavy cannon 4. light cannon 5. rocket launcher """ url = self._get_url('defense', planet) resp = self.br.open(url) for t in ('406', '404', '403', '402', '401'): self.br.select_form(name='form') self.br.form.new_control('text', 'menge', {'value': '100'}) self.br.form.fixup() self.br['menge'] = '100' self.br.form.new_control('text', 'type', {'value': t}) self.br.form.fixup() self.br['type'] = t self.br.form.new_control('text', 'modus', {'value': '1'}) self.br.form.fixup() self.br['modus'] = '1' self.br.submit() def get_player_status(self, destination, origin_planet=None): if not destination: return status = {} origin_planet = origin_planet or self.get_closest_planet(destination) galaxy, system, position = destination.split(':') url = self._get_url('galaxyCnt', origin_planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) soup.find(id='galaxytable') planets = soup.findAll('tr', {'class': 'row'}) target_planet = planets[int(position)-1] name_el = target_planet.find('td', 'playername') status['name'] = name_el.find('span').text status['inactive'] = 'inactive' in name_el.get('class', '') return status def find_inactive_nearby(self, planet, radius=15): self.logger.info("Searching idlers near %s in radius %s" % (planet, radius)) nearby_systems = planet.get_nearby_systems(radius) idlers = [] for system in nearby_systems: galaxy, system = system.split(":") url = self._get_url('galaxyCnt', planet) data = urlencode({'galaxy': galaxy, 'system': system}) resp = self.br.open(url, data=data) soup = BeautifulSoup(resp) galaxy_el = soup.find(id='galaxytable') planets = galaxy_el.findAll('tr', {'class': 'row'}) for pl in planets: name_el = pl.find('td', 'playername') debris_el = pl.find('td', 'debris') inactive = 'inactive' in name_el.get('class', '') debris_not_found = 'js_no_action' in debris_el.get('class', '') if not inactive or not debris_not_found: continue position = pl.find('td', 'position').text coords = "%s:%s:%s" % (galaxy, system, position) player_id = name_el.find('a').get('rel') player_info = soup.find(id=player_id) rank_el = player_info.find('li', 'rank') if not rank_el: continue rank = int(rank_el.find('a').text) if rank > 4000 or rank < 900: continue idlers.append(coords) time.sleep(2) return idlers def find_inactives(self): inactives = [] for p in self.planets: try: idlers = self.find_inactive_nearby(p) self.logger.info(" ".join(idlers)) inactives.extend(idlers) except Exception as e: self.logger.exception(e) continue time.sleep(5) self.logger.info(" ".join(inactives)) self.inactives = list(set(inactives)) self.logger.info(inactives) def send_fleet(self, origin_planet, destination, fleet={}, resources={}, mission='Attack', target='Planet', speed=None, holdingtime=None): if origin_planet.coords == destination: self.logger.error('Cannot send fleet to the same planet') return False self.logger.info('Sending fleet from %s to %s (%s)' % (origin_planet, destination, mission)) try: resp = self.br.open(self._get_url('fleet', origin_planet)) try: self.br.select_form( predicate=lambda f: 'action' in f.attrs and f.attrs ['action'] == 'game.php?page=fleet1') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False #resp=open("samples/fleet.html", "r").read() soup = BeautifulSoup(resp) sended = set() for c in soup.find('form', {'action': 'game.php?page=fleet1'}).findAll('tr')[2:-2]: name = c.find('a').contents[0].strip() if name in fleet: inp = c.find('input')['name'].strip() sended.add(name) self.br.form[inp] = str(fleet[name]) for name in fleet.iterkeys(): if name not in sended: self.logger.info("Couldn't send all ships to mission") return False self.br.submit() try: self.br.select_form( predicate=lambda f: 'action' in f.attrs and f.attrs ['action'] == 'game.php?page=fleet2') except mechanize.FormNotFoundError: self.logger.info('Error while sending ships, fleet2') return False galaxy, system, position = destination.split(':') self.br.form['galaxy'] = galaxy self.br.form['system'] = system self.br.form['planet'] = position self.br.form['planettype'] = [self.TARGETS[target]] if speed: self.br.form['speed'][0] = str(floor(int(speed)/10)) self.br.submit() try: self.br.select_form( predicate=lambda f: 'action' in f.attrs and f.attrs ['action'] == 'game.php?page=fleet3') except mechanize.FormNotFoundError: self.logger.info('Error while sendind ships, fleet 3') return False self.br.form['mission'] = [self.MISSIONS[mission]] if holdingtime: self.br.form['holdingtime'] = [str(holdingtime)] if 'Metal' in resources: self.br.form['resource1'] = str(resources['Metal']) if 'Crystal' in resources: self.br.form['resource2'] = str(resources['Crystal']) if 'Deuterium' in resources: self.br.form['resource3'] = str(resources['Deuterium']) self.br.submit() last_response = self.br.response() # This is returned by br.open(...) too print last_response.geturl() print last_response.info() except Exception as e: self.logger.exception(e) return False return True def send_message(self, url, player, subject, message): self.logger.info('Sending message to %s: %s' % (player, message)) self.br.open(url) self.br.select_form(nr=0) self.br.form['betreff'] = subject self.br.form['text'] = message self.br.submit() def send_sms(self, msg): from smsapigateway import SMSAPIGateway try: SMSAPIGateway().send(msg) except Exception as e: self.logger.exception(str(e)) def handle_attacks(self): attack_opts = options['attack'] send_sms = bool(options['sms']['send_sms']) for a in self.active_attacks: if a.is_dangerous(): self.logger.info('Handling attack: %s' % a) if not a.planet.is_moon(): self.build_defense(a.planet) if send_sms and not a.sms_sent: self.send_sms(a.get_sms_text()) a.sms_sent = True if send_sms and not a.message_sent: self.send_message( a.message_url, a.player, attack_opts['message_topic'], a.get_random_message()) a.message_sent = True self.fleet_save(a.planet) def check_attacks(self, soup): alert = soup.find(id='attack_alert') if not alert: self.logger.exception('Check attack failed') return if 'noAttack' in alert.get('class', ''): self.logger.info('No attacks') self.active_attacks = [] else: self.logger.info('ATTACK!') resp = self.br.open(self.PAGES['events']) soup = BeautifulSoup(resp) hostile = False try: for tr in soup.findAll('tr'): countDown = tr.find('td', 'countDown') if countDown and 'hostile' in countDown.get('class', ''): hostile = True # First: check if attack was noticed if tr.get('id'): attack_id = tr.get('id').split('-')[1] elif countDown.get('id'): attack_id = countDown.get('id').split('-')[2] if not attack_id or attack_id in [a.id for a in self.active_attacks]: continue try: # Attack first discovered: save attack info arrivalTime = tr.find( 'td', 'arrivalTime').text.split(' ')[0] coordsOrigin = tr.find('td', 'coordsOrigin') if coordsOrigin: if coordsOrigin.find('a'): coordsOrigin = coordsOrigin.find( 'a').text.strip()[1:-1] destCoords = tr.find('td', 'destCoords') if destCoords: destCoords = destCoords.find( 'a').text.strip()[1:-1] originFleet = tr.find('td', 'originFleet') detailsFleet = int( tr.find( 'td', 'detailsFleet').span.text.replace( '.', '')) player_info = originFleet.find('a') message_url = player_info.get('href') player = player_info.get('data-player-name') is_moon = False # TODO! planet = self.find_planet( coords=destCoords, is_moon=is_moon) a = Attack( planet, attack_id, arrivalTime, coordsOrigin, destCoords, detailsFleet, player, message_url) self.active_attacks.append(a) except Exception as e: self.logger.exception(e) self.send_sms('ATTACKEROR') if not hostile: self.active_attacks = [] except Exception as e: self.logger.exception(e) def fleet_save(self, p): if not p.has_ships(): return fleet = p.ships # recyclers are staying! #fleet['rc'] = 0 self.logger.info('Making fleet save from %s' % p) self.send_fleet(p, self.get_safe_planet(p).coords, fleet=fleet, mission='Hold Position', speed=10, resources={'metal': p.resources['metal']+500, 'crystal': p.resources['crystal']+500, 'deuterium': p.resources['deuterium']+500}) def collect_debris(self, p): if not p.has_ships(): return self.logger.info( 'Collecting debris from %s using %s recyclers' % (p, p.ships['rc'])) self.send_fleet(p, p.coords, fleet={'rc': p.ships['rc']}, mission='collect', target='debris') def send_expedition(self): expedition = options['expedition'] planets = expedition['planets'].split(' ') random.shuffle(planets) for coords in planets[:3]: planet = self.find_planet(coords=coords) if planet: galaxy, system, position = planet.coords.split(':') expedition_coords = '%s:%s:16' % (galaxy, system) self.send_fleet( planet, expedition_coords, fleet={ expedition['ships_kind']: expedition['ships_number']}, mission='expedition') def farm(self): if options['farming'].has_key('enabled') and not strtobool(options['farming']['enabled']): return farms =[s.strip().split(' ')[0] for s in options['farming']['farms'].split(',')] if not farms or not farms[0]: return ships_kind = options['farming']['ships_kind'] ships_number = options['farming']['ships_number'] next_farm = int(options['farming']['next_farm']) % len(farms) farm = farms[next_farm] #if not self.get_player_status(farm)['inactive']: # self.logger.error('farm %s seems not to be inactive!', farm) # return if self.send_fleet( self.get_closest_planet(farm), farm, fleet={ships_kind: ships_number} ): next_farm = (next_farm+1) % len(farms) options.change_item('farming', 'next_farm', str(next_farm)) def sleep(self): sleep_options = options['general'] sleep_time = randint( 0, int(sleep_options['seed']))+int(sleep_options['check_interval']) self.logger.info('Sleeping for %s secs' % sleep_time) if self.active_attacks: sleep_time = 60 time.sleep(sleep_time) def stop(self): self.logger.info('Stopping bot') os.unlink(self.pidfile) def interactive(self): self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) if not self.login(): self.logger.error('Login failed!') self.load_planets() def start(self): self.logger.info('Starting bot') self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) # main loop while True: if self.login(): try: self.load_planets() self.handle_planets() # self.find_inactives() # if not self.active_attacks: # if True or not self.transport_resources(): # self.send_expedition() # self.farm() # self.farm() # else: # self.handle_attacks() except Exception as e: self.logger.exception(e) # self.stop() # return else: self.logger.error('Login failed!') # self.stop() # return self.sleep()
class Bot(object): HEADERS = [('User-agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36')] RE_BUILD_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\', null, 1\)") RE_SERVER_TIME = re.compile(r"var serverTime=new Date\((.*)\);var localTime") # ship -> ship id on the page SHIPS = { 'lm': '204', 'hm': '205', 'cr': '206', 'ow': '207', 'pn': '215', 'bb': '211', 'ns': '213', 'gs': '214', 'lt': '202', 'dt': '203', 'cs': '208', 'rc': '209', 'ss': '210' } # mission ids MISSIONS = { 'attack': '1', 'transport': '3', 'station': '4', 'expedition': '15', 'collect': '8' } TARGETS = { 'planet': '1', 'moon': '3', 'debris': '2' } SPEEDS = { 100: '10', 90: '9', 80: '8', 70: '7', 60: '6', 50: '5', 40: '4', 30: '3', 20: '2', 10: '1' } RESOURCESTOSEND = { 'metal' : 0, 'crystal' : 0, 'deuterium' : 0 } def __init__(self, username=None, password=None, server=None): self.server = server self.username = username self.password = password self.logged_in = False self._prepare_logger() self._prepare_browser() self.round = 0 # Comandi gestiti dal bot self.chatIdTelegram = options['credentials']['chat_id_telegram'] self.botTelegram = options['credentials']['bot_telegram'] self.CMD_STOP = False self.CMD_PING = False self.CMD_FARM = True self.CMD_LOGIN = True self.CMD_GET_FARMED_RES = False n = 1 self.farm_no = [] self.bn_farms = 'farms_' self.bn_from_planet = 'from_planet_' loop = True while loop: try: farms = options['farming'][self.bn_farms + str(n)].split(' ') self.farm_no.append((randint(0, len(farms) - 1) if farms else 0)) from_planet = options['farming'][self.bn_from_planet + str(n)] self.logger.info("Pianeta: " + from_planet + " Inizio dalla farm n: " + str(self.farm_no[n - 1])) n += 1 except Exception as e: loop = False self.MAIN_URL = 'https://' + self.server + '/game/index.php' self.PAGES = { 'main': self.MAIN_URL + '?page=overview', 'resources': self.MAIN_URL + '?page=resources', 'station': self.MAIN_URL + '?page=station', 'research': self.MAIN_URL + '?page=research', 'shipyard': self.MAIN_URL + '?page=shipyard', 'defense': self.MAIN_URL + '?page=defense', 'fleet': self.MAIN_URL + '?page=fleet1', 'galaxy': self.MAIN_URL + '?page=galaxy', 'galaxyCnt': self.MAIN_URL + '?page=galaxyContent', 'events': self.MAIN_URL + '?page=eventList', 'messages': self.MAIN_URL + '?page=messages', } self.planets = [] self.moons = [] self.active_attacks = [] self.fleet_slots = 0 self.active_fleets = 0 self.server_time = self.local_time = datetime.now() self.time_diff = 0 self.emergency_sms_sent = False self.transport_manager = TransportManager() self.sim = Sim() self.INSULTI_A_JONNY = [ 'Jhonny sei utile come una sonda contro una rip', 'Jhonny stanotte ti ho sognato mentre ti salutavo dall alto. Tu eri bello abbronzato e immerso nell acqua, poi ho tirato lo scarico e sei sparito.', 'Jhonny ti informo che da oggi puoi acquistare a soli 18 euro il kit di espansione del tuo cervello. Prova anche tu il piacere di formulare frasi e pensieri corretti.', 'Jhonny sei cosi grosso che una mosca per farti un giro intorno muore di vecchiaia.', 'Jhonny ricordati che di gente come te ho ancora i pezzi in frigo', 'Jhonny le pause tra i tuoi discorsi sono le cose più interessanti che dici', 'Jhonny se tu in questo momento ingerissi un moscerino avresti più cervello nello stomaco che in testa!', 'Jhonny abbraccia la tazza del cesso e cantagli : "non son degno di te". ', 'Jhonny devi aver fatto la fila tre volte, quando il buon Dio ha distribuito la stupidità !', 'Jhonny, ma da piccolo i tuoi genitori ti lanciavano in aria e non ti prendevano?', 'Jhonny il mondo è una merda. Tu sì che sei un uomo di mondo', ] def _get_url(self, page, planet=None): url = self.PAGES[page] if planet is not None: url += '&cp=%s' % planet.id return url def _prepare_logger(self): self.logger = logging.getLogger("mechanize") fh = RotatingFileHandler('bot.log', maxBytes=100000, backupCount=5) sh = logging.StreamHandler() fmt = logging.Formatter(fmt='%(asctime)s %(levelname)s %(message)s', datefmt='%m-%d, %H:%M:%S') fh.setFormatter(fmt) sh.setFormatter(fmt) self.logger.setLevel(logging.INFO) self.logger.addHandler(fh) self.logger.addHandler(sh) def _prepare_browser(self): # Instantiate a Browser and set the cookies self.br = mechanize.Browser() self.br.set_handle_equiv(True) self.br.set_handle_redirect(True) self.br.set_handle_referer(True) self.br.set_handle_robots(False) self.br.addheaders = self.HEADERS def _parse_build_url(self, js): """ convert: `sendBuildRequest('url', null, 1)`; into: `url` """ return self.RE_BUILD_REQUEST.findall(js)[0] def _parse_server_time(self, content): return self.RE_SERVER_TIME.findall(content)[0] def get_mother(self): for p in self.planets: if p.mother: return p return p[0] if self.planets else None def find_planet(self, name=None, coords=None, id=None, is_moon=None): if is_moon: planets = self.moons else: planets = self.planets for p in planets: if name == p.name or coords == p.coords or id == p.id: return p def login_lobby(self, username=None, password=None, server=None): username = username or self.username password = password or self.password server = server or self.server player_id = options['credentials']['player_id'] number = server[1:4] try: driver = webdriver.Chrome() driver.get("https://it.ogame.gameforge.com") # Chiudo banner try: driver.find_element_by_link_text("x").click() except: self.logger.info('No banner found') # Vado sulla Login Form driver.find_element_by_link_text("Login").click() # Immetto Credenziali usernameLogin = driver.find_element_by_id("usernameLogin") passwordLogin = driver.find_element_by_id("passwordLogin") usernameLogin.send_keys(username) passwordLogin.send_keys(password) # Clicco su login driver.find_element_by_id("loginSubmit").click() time.sleep(2) # Recupero URL login driver.get( "https://lobby-api.ogame.gameforge.com/users/me/loginLink?id=" + player_id + "&server[language]=it&server[number]=" + number) time.sleep(2) # Richiamo il login html = driver.page_source soup = BeautifulSoup(html) url = 'https://' + server + '/game/lobbylogin.php?' + soup.find('pre').text.split('?')[1].replace('"}','').replace('&', '&') driver.get(url) # Passo i cookie e la sessione a mechanize cookie = driver.get_cookies() cj = cookielib.LWPCookieJar() for s_cookie in cookie: cj.set_cookie(cookielib.Cookie(version=0, name=s_cookie['name'], value=s_cookie['value'], port='80', port_specified=False, domain=s_cookie['domain'], domain_specified=True, domain_initial_dot=False, path=s_cookie['path'], path_specified=True, secure=s_cookie['secure'], expires=None, discard=False, comment=None, comment_url=None, rest=None, rfc2109=False)) self.br.set_cookiejar(cj) except Exception as e: self.logger.exception(e) self.logged_in = False return False # Chiudo il browser driver.quit() self.logged_in = True return True def calc_time(self, resp): try: y, mo, d, h, mi, sec = map(int, self._parse_server_time(resp).split(',')) except: self.logger.error('Exception while calculating time') else: self.local_time = n = datetime.now() self.server_time = datetime(n.year, n.month, n.day, h, mi, sec) self.time_diff = self.server_time - self.local_time self.logger.info('Server time: %s, local time: %s' %(self.server_time, self.local_time)) def fetch_planets(self): self.logger.info('Fetching planets..') resp = self.br.open(self.PAGES['main']).read() self.calc_time(resp) soup = BeautifulSoup(resp) self.planets = [] self.moons = [] try: for i, c in enumerate(soup.findAll('a', 'planetlink')): name = c.find('span', 'planet-name').text coords = c.find('span', 'planet-koords').text[1:-1] url = c.get('href') p_id = int(c.parent.get('id').split('-')[1]) construct_mode = len(c.parent.findAll('a', 'constructionIcon')) != 0 p = Planet(p_id, name, coords, url, construct_mode) if i == 0: p.mother = True self.planets.append(p) # check if planet has moon moon = c.parent.find('a', 'moonlink') if moon and 'moonlink' in moon['class']: url = moon.get('href') m_id = url.split('cp=')[1] m = Moon(m_id, coords, url) self.moons.append(m) except: self.logger.exception('Exception while fetching planets') else: self.check_attacks() def handle_planets(self): self.fetch_planets() for p in iter(self.planets): self.update_planet_info(p) self.update_planet_fleet(p) for m in iter(self.moons): self.update_planet_info(m) self.update_planet_fleet(m) def update_planet_fleet(self, planet): resp = self.br.open(self._get_url('fleet', planet)) soup = BeautifulSoup(resp) ships = {} for k, v in self.SHIPS.iteritems(): available = 0 try: s = soup.find(id='button' + v) available = int(s.find('span', 'textlabel').nextSibling.replace('.', '')) except: available = 0 ships[k] = available planet.ships = ships def update_planet_resources_farmed(self, planet): try: resp = self.br.open(self._get_url('fleet', planet)) soup = BeautifulSoup(resp) metal = int(soup.find(id='resources_metal').text.replace('.', '')) - int(planet.resources['metal']) crystal = int(soup.find(id='resources_crystal').text.replace('.', '')) - int(planet.resources['crystal']) deuterium = int(soup.find(id='resources_deuterium').text.replace('.', '')) - int(planet.resources['deuterium']) text = 'Pianeta: ' + str(planet.coords) + \ '\n\t\t\tTotale risorse farmate: ' + "{:,}".format(metal + crystal + deuterium) + \ '\n\t\t\t\t\t\tMetallo: ' + "{:,}".format(metal) + \ '\n\t\t\t\t\t\tCristallo: ' + "{:,}".format(crystal) + \ '\n\t\t\t\t\t\tDeuterio: ' + "{:,}".format(deuterium) + '\n\n' except: text = 'Exception while updating resources info' return text def update_planet_info(self, planet): self.miniSleep() self.logger.info('Carico le risorse del pianeta: ' + planet.coords) resp = self.br.open(self._get_url('resources', planet)) soup = BeautifulSoup(resp) today = datetime.today().strftime('%Y-%m-%d') found = False if os.path.isfile('resources_'+today+'.txt'): file = open('resources_'+today+'.txt', 'r') for line in file: if line.split('/')[0] == planet.coords: found = True planet.resources['metal'] = line.split('/')[1] planet.resources['crystal'] = line.split('/')[2] planet.resources['deuterium'] = line.split('/')[3] file.close() if found == False: file = open('resources_' + today + '.txt', 'a') metal = int(soup.find(id='resources_metal').text.replace('.', '')) planet.resources['metal'] = metal crystal = int(soup.find(id='resources_crystal').text.replace('.', '')) planet.resources['crystal'] = crystal deuterium = int(soup.find(id='resources_deuterium').text.replace('.', '')) planet.resources['deuterium'] = deuterium energy = int(soup.find(id='resources_energy').text.replace('.', '')) planet.resources['energy'] = energy file.write(str(planet.coords) + '/' + str(metal) + '/' + str(crystal) + '/' + str(deuterium) + '\n') file.close() else: # Per ora carico solo le risorse. Il resto non serve try: file = open('resources_' + today + '.txt', 'w') metal = int(soup.find(id='resources_metal').text.replace('.', '')) planet.resources['metal'] = metal crystal = int(soup.find(id='resources_crystal').text.replace('.', '')) planet.resources['crystal'] = crystal deuterium = int(soup.find(id='resources_deuterium').text.replace('.', '')) planet.resources['deuterium'] = deuterium energy = int(soup.find(id='resources_energy').text.replace('.', '')) planet.resources['energy'] = energy file.write(str(planet.coords)+'/'+str(metal)+'/'+str(crystal)+'/'+str(deuterium)+'\n') file.close() except: self.logger.exception('Exception while updating resources info') def update_planet_resources(self, planet): self.miniSleep() try: resp = self.br.open(self._get_url('resources', planet)) soup = BeautifulSoup(resp) metal = int(soup.find(id='resources_metal').text.replace('.', '')) self.RESOURCESTOSEND['metal']=metal crystal = int(soup.find(id='resources_crystal').text.replace('.', '')) self.RESOURCESTOSEND['crystal'] = crystal deuterium = int(soup.find(id='resources_deuterium').text.replace('.', '')) self.RESOURCESTOSEND['deuterium'] = deuterium except: self.logger.exception('Exception while updating resources info') return True def transport_resources(self): tasks = self.transport_manager.find_dest_planet(self.planets) if tasks is None: return False self.logger.info(self.transport_manager.get_summary()) for task in iter(tasks): self.logger.info('Transport attempt from: %s, to: %s with resources %s' \ % (task['from'], task['where'], task['resources'])) result = self.send_fleet( task['from'], task['where'].coords, fleet=task['from'].get_fleet_for_resources(task['resources']), resources=task['resources'], mission='transport' ) if result: self.transport_manager.update_sent_resources(task['resources']) self.logger.info('Resources sent: %s, resources needed: %s' \ % (task['resources'], self.transport_manager.get_resources_needed())) return True def send_fleet(self, origin_planet, destination, fleet={}, resources={},mission='attack', target='planet', speed=10): if origin_planet.coords == destination: self.logger.error('Cannot send fleet to the same planet') return False self.logger.info('Sending fleet from %s to %s (%s)' % (origin_planet, destination, mission)) try: resp = self.br.open(self._get_url('fleet', origin_planet)) try: self.br.select_form(name='shipsChosen') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False # Controllo slot flotta soup = BeautifulSoup(resp) span = soup.find('span', title='Slots flotta Usati/Totali') text = span.text.split(':')[1] usati = text.split('/')[0] disponibili = text.split('/')[1] if usati == disponibili: self.logger.info('No free slots (' + usati + '/' + disponibili + ')') return False for ship, num in fleet.iteritems(): s = soup.find(id='button' + self.SHIPS[ship]) num = int(num) try: available = int(s.find('span', 'textlabel').nextSibling.replace('.', '')) except: available = 0 if available < num and mission in ('attack', 'expedition'): self.logger.info('No available ships to send') return False if num > 0: self.br.form['am' + self.SHIPS[ship]] = str(num) self.miniSleep() self.br.submit() try: self.br.select_form(name='details') except mechanize.FormNotFoundError: self.logger.info('No available ships on the planet') return False galaxy, system, position = destination.split(':') self.br['galaxy'] = galaxy self.br['system'] = system self.br['position'] = position self.br.form.find_control("type").readonly = False self.br['type'] = self.TARGETS[target] self.br.form.find_control("speed").readonly = False self.br['speed'] = speed self.miniSleep() try: self.br.submit() self.br.select_form(name='sendForm') except Exception as e: self.send_telegram_message("Errore selezione pianeta " + destination + ": Verificare che esista ancora.") return False self.br.form.find_control("mission").readonly = False self.br.form['mission'] = self.MISSIONS[mission] if 'metal' in resources: self.br.form['metal'] = str(resources['metal']) if 'crystal' in resources: self.br.form['crystal'] = str(resources['crystal']) if 'deuterium' in resources: self.br.form['deuterium'] = str(resources['deuterium']) self.miniSleep() self.br.submit() self.miniSleep() except Exception as e: self.logger.exception(e) return False return True def send_message(self, url, player, subject, message): self.logger.info('Sending message to %s: %s' % (player, message)) self.br.open(url) self.br.select_form(nr=0) self.br.form['betreff'] = subject self.br.form['text'] = message self.br.submit() def check_attacks(self): resp = self.br.open(self.PAGES['main']).read() soup = BeautifulSoup(resp) alert = soup.find(id='attack_alert') if not alert: self.logger.exception('Check attack failed') return if 'noAttack' in alert.get('class', ''): self.logger.info('No attacks') self.active_attacks = [] else: self.logger.info('ATTACK!') resp = self.br.open(self.PAGES['events']) soup = BeautifulSoup(resp) hostile = False attack_id = 0 text = '' arrivalTime = '' originCoords = [] destCoords = '' player = [] attackNew = False try: for tr in soup.findAll('tr'): countDown = tr.find('td', 'countDown') if countDown and 'hostile' in countDown.get('class', ''): hostile = True # First: check if attack was noticed if tr.get('id'): attack_id = tr.get('id').split('-')[1] elif countDown.get('id'): attack_id = countDown.get('id').split('-')[2] if not attack_id or attack_id in [a.id for a in self.active_attacks]: continue if tr.get('class').split(' ')[0] == 'allianceAttack': typeAttack = 'ATTACCO FEDERATO' else: typeAttack = 'ATTACCO' if str(typeAttack) != str('ATTACCO FEDERATO') and tr.get('class').split(' ')[ 0] != 'partnerInfo': attackNew = True try: # Attack first discovered: save attack info arrivalTime = tr.find('td', 'arrivalTime').text.split(' ')[0] coordsOrigin = tr.find('td', 'coordsOrigin') if coordsOrigin: if coordsOrigin.find('a'): originCoords.append(coordsOrigin.find('a').text.strip()[1:-1]) destCoords = tr.find('td', 'destCoords') if destCoords: destCoords = destCoords.find('a').text.strip()[1:-1] detailsFleet.append(tr.find('td', 'detailsFleet').span.text.replace('.', '')) player.append(tr.find('td', 'sendMail').find('a').get('title')) except Exception as e: self.logger.exception(e) elif typeAttack == 'ATTACCO FEDERATO' or tr.get('class').split(' ')[0] == 'partnerInfo': if tr.get('class').split(' ')[0] == 'partnerInfo': coordsOrigin = tr.find('td', 'coordsOrigin') if coordsOrigin: if coordsOrigin.find('a'): originCoords.append(coordsOrigin.find('a').text.strip()) player.append(tr.find('td', 'sendMail').find('a').get('title')) detailsFleet.append(tr.find('td', 'detailsFleet').span.text.replace('.', '')) else: attackNew = True arrivalTime = tr.find('td', 'arrivalTime').text.split(' ')[0] destCoords = tr.find('td', 'destCoords') if destCoords: destCoords = destCoords.find('a').text.strip()[1:-1] detailsFleet = tr.find('td', 'detailsFleet').span.text.replace('.', '') if attackNew: text = text + '\n\n' + str(typeAttack) + ' IN CORSO\n' \ 'Orario di arrivo: ' + str(arrivalTime) + '\n' \ 'Coordinate di arrivo: ' + str(destCoords) + '\n' for i in range(0, len(player), 1): text = text + '\t\t\t\t\tGIOCATORE: ' + str(player[i]) + '\n' \ '\t\t\t\t\tCoordinate di partenza: ' + str(originCoords[i]) + '\n' \ '\t\t\t\t\tNumero navi in arrivo: ' + str(detailsFleet[i]) + '\n' arrivalTime = '' destCoords = '' detailsFleet = [] player = [] attackNew = False self.send_telegram_message(text) if not hostile: self.active_attacks = [] except Exception as e: self.logger.exception(e) def send_telegram_message(self, message): url = 'https://api.telegram.org/' + str(self.botTelegram) + '/sendMessage?' if self.chatIdTelegram != '': data = urlencode({'chat_id': self.chatIdTelegram, 'text': message}) self.br.open(url, data=data) def collect_debris(self, p): if not p.has_ships(): return self.logger.info('Collecting debris from %s using %s recyclers' % (p, p.ships['rc'])) self.send_fleet(p, p.coords, fleet={'rc': p.ships['rc']}, mission='collect', target='debris') def send_expedition(self): expedition = options['expedition'] planets = expedition['planets'].split(' ') random.shuffle(planets) for coords in planets[:3]: planet = self.find_planet(coords=coords) if planet: galaxy, system, position = planet.coords.split(':') expedition_coords = '%s:%s:16' % (galaxy, system) self.send_fleet(planet, expedition_coords, fleet={expedition['ships_kind']: expedition['ships_number']}, mission='expedition') def get_command_from_telegram_bot(self): import json import time chatIdTelegram = options['credentials']['chat_id_telegram'] botTelegram = options['credentials']['bot_telegram'] lastUpdateIdTelegram = options['credentials']['last_update_id'] url = 'https://api.telegram.org/' + str(botTelegram) + '/getUpdates?offset=' + str(int(lastUpdateIdTelegram)+1) resp = self.br.open(url) soup = BeautifulSoup(resp) data_json = json.loads(str(soup)) result = data_json['result'] for id in range(0, len(result)): timeMessage = result[id]['message']['date'] chatId = result[id]['message']['chat']['id'] text = result[id]['message']['text'] update_id = result[id]['update_id'] currentTime = int(time.time()) - 300 if timeMessage > currentTime and chatId == int(chatIdTelegram): options.updateValue('credentials', 'last_update_id', str(update_id)) if text == '/resourcesfarmed': self.CMD_GET_FARMED_RES = True elif text == '/ping': self.send_telegram_message("Pong") elif text == '/jhonny': self.send_telegram_message(self.INSULTI_A_JONNY[randint(0, len(self.INSULTI_A_JONNY))]) elif text == '/stop': self.CMD_STOP = True elif text == '/stop_farmer': self.CMD_FARM = False self.send_telegram_message('Farmer fermato.') elif text == '/start_farmer': self.CMD_FARM = True self.send_telegram_message('Farmer riattivato.') elif text == '/is_logged': self.send_telegram_message("Loggato: " + str(self.logged_in)) elif text == '/login': self.CMD_LOGIN = True elif text == '/logout': self.logged_in =False self._prepare_browser() elif text.split(' ')[0] == '/raccolta': target = text.split(' ')[1] self.send_transports_production(target) self.logger.info('All planets send production to ' + str(target)) elif text.split(' ')[0] == '/attack_probe': target = text.split(' ')[1] self.send_attack_of_probe(target) self.logger.info('Attack of probes to ' + str(target) + ' sended') # # Invio farmata di sonde # def farm(self): # Carico settings ships_kind = options['farming']['ships_kind'] ships_number = options['farming']['ships_number'] speed = options['farming']['ships_speed'] # Ciclo sui pianeti da farmare n = 1 farms = options['farming'][self.bn_farms + str(n)].split(' ') from_planet = options['farming'][self.bn_from_planet + str(n)] loop = True while loop: # Seleziono pianeta di attacco planet = self.find_planet(coords=from_planet, is_moon=True) # Controllo che ci siano farm l = len(farms) if not (l == 0 or not farms[0]): # Seleziono la prossima farm da attaccare farm = farms[self.farm_no[n - 1] % l] # Invio attacchi finche ci sono navi while self.send_fleet(planet,farm,fleet={ships_kind: ships_number},speed=speed): self.farm_no[n - 1] += 1 farm = farms[self.farm_no[n - 1] % l] n += 1 try: farms = options['farming'][self.bn_farms + str(n)].split(' ') from_planet = options['farming'][self.bn_from_planet + str(n)] except Exception as e: loop = False def send_transports_production(self,target): for planet in self.planets: self.update_planet_resources(planet) numFleet = (self.RESOURCESTOSEND['metal']+self.RESOURCESTOSEND['crystal']+self.RESOURCESTOSEND['deuterium'])/25000 if int(numFleet) > 150: self.send_fleet(planet, target, fleet={'dt':numFleet}, resources = self.RESOURCESTOSEND, mission='transport',target='planet', speed='10') def send_farmed_res(self): response = '' n = 1 from_planet = options['farming'][self.bn_from_planet + str(n)] loop = True try: while loop: planet = self.find_planet(coords=from_planet, is_moon=True) response = response + self.update_planet_resources_farmed(planet) n += 1 try: from_planet = options['farming'][self.bn_from_planet + str(n)] except: loop = False except Exception as e: self.logger.exception(e) response = "Errore lettura risorse farmate: " + e.message.decode() self.send_telegram_message(response) self.CMD_GET_FARMED_RES = False def sleep(self): sleep_options = options['general'] min = int(sleep_options['seed']) - randint(0, int(sleep_options['check_interval'])) max = int(sleep_options['seed']) + randint(0, int(sleep_options['check_interval'])) sleep_time = randint(min, max) self.logger.info('Sleeping for %s secs' % sleep_time) if self.active_attacks: sleep_time = 60 time.sleep(sleep_time) def miniSleep(self): mini_sleep_time = randint(400, 2500) / 1000 time.sleep(mini_sleep_time) def stop(self): self.logger.info('Stopping bot') os.unlink(self.pidfile) def send_attack_of_probe(self,target): attack= True for planet in self.planets: if attack: if self.send_fleet(planet, target, fleet={'ss': '1'}, speed='10'): attack = False break for moon in self.moons: if attack: if self.send_fleet(moon, target, fleet={'ss': '1'}, speed='10'): attack = False break def load_farming_planets_info(self): response = '' n = 1 from_planet = options['farming'][self.bn_from_planet + str(n)] loop = True try: while loop: planet = self.find_planet(coords=from_planet, is_moon=True) self.update_planet_info(planet) try: n += 1 from_planet = options['farming'][self.bn_from_planet + str(n)] except: loop = False except Exception as e: self.logger.exception(e) def refresh_mother(self): self.round = self.round + 1 if self.round % 5 == 0: self.br.open(self._get_url('main', self.get_mother())) self.logger.info("Mother refreshed") self.send_telegram_message("BOT ATTIVO") def start(self): self.logger.info('Starting bot') self.pid = str(os.getpid()) self.pidfile = 'bot.pid' file(self.pidfile, 'w').write(self.pid) while(not self.CMD_STOP): try: self.get_command_from_telegram_bot() if(self.CMD_LOGIN): self.login_lobby() if(self.logged_in): self.fetch_planets() self.load_farming_planets_info() self.CMD_LOGIN = False if(self.logged_in): self.refresh_mother() if (self.CMD_GET_FARMED_RES): self.send_farmed_res() if(self.CMD_FARM): self.check_attacks() self.farm() except Exception as e: self.logger.exception(e) self.send_telegram_message("Errore: " + str(e.message)) self.sleep() self.send_telegram_message("Bot Spento") self.stop()