Exemple #1
0
    def __init__(self, username=None, password=None, uni='145'):
        self.uni = uni
        self.username = username
        self.password = password
        self.logged_in = False

        self._prepare_logger()
        self._prepare_browser()

        self.MAIN_URL = 'https://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.transport_manager = TransportManager()
        self.server_time = self.local_time = datetime.now()
        self.time_diff = 0
        self.sim = Sim()
Exemple #2
0
    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()
Exemple #3
0
    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-%s.ogame.gameforge.com/game/index.php' % (
            self.uni, options['credentials']['server'])
        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',
            'resSettings': self.MAIN_URL + '?page=resourceSettings',
        }
        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()
Exemple #4
0
    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()
Exemple #5
0
    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()
Exemple #6
0
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()
Exemple #7
0
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()
Exemple #8
0
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('&nbsp;')[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()
Exemple #9
0
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('&nbsp;')[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()
Exemple #10
0
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 (X11; Linux x86_64; rv:45.0) Gecko/20100101 		Firefox/45.0'
    )]
    RE_BUILD_REQUEST = re.compile(r"sendBuildRequest\(\'(.*)\', null, 1\)")
    RE_SERVER_TIME = re.compile(
        r"var serverTime=new Date\((.*)\);var localTime")
    LOGOUT_URL = 'https://s145-pl.ogame.gameforge.com/game/index.php?page=logout'

    def __init__(self, username=None, password=None, uni='145'):
        self.uni = uni
        self.username = username
        self.password = password
        self.logged_in = False

        self._prepare_logger()
        self._prepare_browser()

        self.MAIN_URL = 'https://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.transport_manager = TransportManager()
        self.server_time = self.local_time = datetime.now()
        self.time_diff = 0
        self.sim = Sim()

    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 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 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)
                time.sleep(3)

                # 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 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 handle_planets(self):
        self.fetch_planets()

        for p in iter(self.planets):
            if p.mother:
                self.update_planet_laboratory(self, p)
                time.sleep(randint(2, 8))
            self.update_planet_info(p)
            time.sleep(randint(2, 8))
            # 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)
        time.sleep(randint(3, 15))

        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
            storageList = soup.find(id='storage')
            storages = ('metalStorage', 'crystalStorage', 'deuterStorage')
            for storage, s in zip(storages, storageList.findAll('li')):
                can_build = 'on' in s.get('class')
                fb = s.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(s.find('span', 'textlabel').nextSibling)
                    capacity = int(
                        float(
                            int(12.5 * math.exp(20 * float(level) / 33)) / 5) *
                        5 * 1000)
                    print level
                    print capacity

                except AttributeError:
                    try:
                        level = int(s.find('span', 'level').text)
                    except:
                        pass
                suff_energy = 0
                res = dict(level=level,
                           build_url=build_url,
                           can_build=can_build,
                           capacity=capacity,
                           sufficient_energy=suff_energy)
                planet.storageBuildings[storage] = res

            if buildingList.find('div', 'construction'):
                in_construction_mode = True
            if storageList.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)
                time.sleep(randint(5, 15))
            else:
                return False
        else:
            self.logger.info('Building queue is not empty')
        return True

    def update_planet_laboratory(self, planet):
        in_research_mode = False
        resp = self.br.open(self._get_url('research', planet))
        soup = BeautifulSoup(resp)
        time.sleep(randint(3, 15))
        if planet.is_moon():
            return

        try:
            researchList = soup.find(id='base1')
            researches = ('energyTech', 'laserTech', 'ionTech', 'plazmTech',
                          'fuelDrive', 'impulsDrive', 'hyperspaceDrive',
                          'spyTech', 'computerTech', 'astroPhysic',
                          'interstellarWeb', 'hyperspaceTech', 'combatTech',
                          'defenceTech', 'armorTech')
            for research, r in zip(researches, researchList.findAll('li')):
                can_build = 'on' in r.get('class')
                fb = r.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(r.find('span', 'textlabel').nextSibling)
                except AttributeError:
                    try:
                        level = int(r.find('span', 'level').text)
                    except:
                        pass
                res = dict(
                    level=level,
                    can_build=can_build,
                    build_url=build_url,
                )

                planet.reserches[researches] = res
            if researchList.find('div', 'construction'):
                in_research_mode = True
        except:
            self.logger.exception('Exception while updating buildings info')
            return False
        else:
            self.logger.info('%s researches were updated' % planet)
        if not in_research_mode:
            text, url = planet.get_reserch_to_upgrade()
            if url:
                self.logger.info('Research upgrade on %s: %s' % (planet, text))
                self.br.open(url)
                planet.in_research_mode = True
                # let now transport manager to clear building queue
                self.transport_manager.update_research(planet)
                time.sleep(randint(5, 15))
            else:
                return False
        else:
            self.logger.info('Building queue is not empty')
        return True

    def _parse_build_url(self, js):
        """
        convert: `sendBuildRequest('url', null, 1)`; into: `url`
        """
        return self.RE_BUILD_REQUEST.findall(js)[0]

    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 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!')

    def _get_url(self, page, planet=None):
        url = self.PAGES[page]
        if planet is not None:
            url += '&cp=%s' % planet.id
        return url

    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 True:
            if self.login():
                try:
                    self.handle_planets()
                except Exception as e:
                    self.logger.exception(e)
                    break
            else:
                self.logger.error('Login failed!')
                break
            self.sleep()
        resp = self.br.open(self.MAIN_URL, timeout=10)
        self.logger.info('LOGOUT')
        quit()

    def exit_gracefully(self, signum, frame):
        signal.signal(signal.SIGINT, original_sigint)
        resp = self.br.open(self.MAIN_URL, timeout=10)
        print('LOGOUT with break')
        quit()
Exemple #11
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',
        ]
Exemple #12
0
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('&amp;', '&')
            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()