def __init__(self, url, username, password, map_file, proxies=None, verify=True): # Ruleset to map fields from MISP to ArcSight # For each field of MISP attribute convert function (example: self._convertComma) will be executed # If dots are in misp field name threat it as a nested dict. Example A.B.C -> misp_entry[A][B][C] self.test = False self.helper = Helper() self.maps = yaml.load(open(map_file, 'r')) if not self._verifyMap(self.maps): logging.error('There is an error in the map file: {}'.format(map_file)) exit() self.default_merge_function = 'space' self.default_convert_function = 'return' self.active_lists = ActiveLists(url, username, password, proxies=proxies, verify=False) if not self.active_lists: logging.error('Could not connect to ArcSight ESM') exit() self.entries = {} self.to_add = {} self.to_delete = {} self.attributes_to_skip = [] for i in range(len(self.maps)): active_list = self.maps[i] # Select active list primary key if there is none if 'primary_key' not in active_list: for rule in active_list['map']: if rule['misp'] == 'value': self.maps[i]['primary_key'] = rule['arcsight'] break self.to_add[active_list['id']] = [] self.to_delete[active_list['id']] = [] self.entries[active_list['id']] = self.active_lists.getEntries(active_list['id'])
def stop_watch(self): """This method needs to be called for rate estimations""" self.__iteration += 1 if Stats.track_xp: Stats.set_value_with_ocr("XP") if not Stats.OCR_failed: cxp = Stats.xp dxp = cxp - self.last_xp self.dxp_log.append(dxp) self.last_xp = cxp else: print("Problems with OCR, skipping stats for this run") self.last_timestamp = time.time() return if Stats.track_pp: Stats.set_value_with_ocr("PP") if not Stats.OCR_failed: cpp = Stats.pp dpp = cpp - self.last_pp self.dpp_log.append(dpp) self.last_pp = cpp else: print("Problems with OCR, skipping stats for this run") self.last_timestamp = time.time() return dtime = time.time() - self.last_timestamp self.dtime_log.append(dtime) self.last_timestamp = time.time() print("This run: {:^8}{:^3}This run: {:^8}".format( Helper.human_format(dxp), "|", Helper.human_format(dpp)))
def buy(self): """Buy upgrades for both attack and defense Requires the confirmation popup button for EXP purchases in settings to be turned OFF. This uses all available exp, so use with caution. """ Stats.set_value_with_ocr("XP") if Stats.OCR_failed: print('OCR failed, exiting upgrade routine.') return current_exp = Stats.xp if current_exp < 1000: return total_price = (coords.RATTACK_COST * self.attack) total_price += (coords.RDEFENSE_COST * self.defense) # Skip upgrading if we don't have enough exp to buy at least one # complete set of upgrades, in order to maintain our perfect ratios :) if total_price > current_exp: if self.report: print("No XP Upgrade :{:^8} of {:^8}".format( Helper.human_format(current_exp), Helper.human_format(total_price))) return amount = int(current_exp // total_price) a_attack = amount * self.attack a_defense = amount * self.defense Navigation.exp_rich() Inputs.click(*coords.EM_ADV_BOX) Inputs.send_string(str(a_attack)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_ADV_BOX) Inputs.send_string(str(a_defense)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_ADV_BOX) Inputs.click(*coords.EM_ADV_BOX) Stats.set_value_with_ocr("XP") total_spent = coords.RATTACK_COST * a_attack total_spent += coords.RDEFENSE_COST * a_defense if self.report: print("Spent XP:{:^8}{:^3}Attack:{:^8}{:^3}Defense:{:^8}".format( Helper.human_format(total_spent), "|", Helper.human_format(a_attack), "|", Helper.human_format(a_defense)))
def __show_progress(self): if self.__iteration == 1: print('Starting: {:^8}{:^3}Starting: {:^8}'.format( Helper.human_format(Stats.xp), "|", Helper.human_format(Stats.pp))) else: elapsed = self.elapsed_time() xph, pph = self.__estimaterate.rates() report_time = "\n{0:^40}\n".format(elapsed) print('Current: {:^8}{:^3}Current: {:^8}'.format( Helper.human_format(Stats.xp), "|", Helper.human_format(Stats.pp))) print('Per hour: {:^8}{:^3}Per hour: {:^8}'.format( Helper.human_format(xph), "|", Helper.human_format(pph))) print(report_time)
"""ITOPOD Sniping script.""" import time # Helper classes from classes.features import Adventure, GoldDiggers, MoneyPit, Inventory from classes.helper import Helper from classes.stats import Tracker import constants as const Helper.init(True) Helper.requirements() tracker = Tracker(5) while True: # main loop titans = Adventure.check_titan_status() if titans: for titan in titans: Adventure.kill_titan(titan) Adventure.itopod_snipe(300) MoneyPit.pit() tracker.progress() GoldDiggers.gold_diggers(const.DEFAULT_DIGGER_ORDER) Inventory.boost_equipment(boost_cube=True) time.sleep(3) # Need to wait for tooltip to disappear
"""24-hour rebirth script.""" import time # Helper classes from classes.features import (AdvancedTraining, Adventure, Augmentation, FightBoss, Inventory, Misc, BloodMagic, GoldDiggers, NGU, Wandoos, TimeMachine, MoneyPit, Rebirth, Questing, Yggdrasil) from classes.helper import Helper # Set these to your own loadouts respawn_loadout = 1 ygg_loadout = 2 Helper.init() Helper.requirements() def rebirth_init(rt): """Procedure that handles start of rebirth.""" Misc.reclaim_all() # make sure we reset e/m if we run this mid-rebirth FightBoss.nuke(101) # PPP Inventory.loadout(respawn_loadout) Adventure.adventure(highest=True) TimeMachine.time_machine(5e11, magic=True) Augmentation.augments({"CI": 0.7, "ML": 0.3}, 1e12) BloodMagic.blood_magic(8) BloodMagic.toggle_auto_spells() GoldDiggers.gold_diggers() if rt.timestamp.tm_hour > 0 or rt.timestamp.tm_min >= 13: print("assigning adv training") else:
def __init__(self, url, key, verify_cert): self.pymisp = PyMISP(url, key, verify_cert) self.helper = Helper()
class Misp: """Based on the rules and priorities pull data from MISP. Conduct filtering, merging, prioritization and manipulation of data from MISP. Example: misp = Misp(config.get('misp-url'), config.get('misp-key'), not config.get('misp-no-verify-cert')) misp.loadRules(config.get('rules-file')) misp.downloadOrganisations() misp.loadPriorities(config.get('priorities-file')) misp.downloadAttributes(config.get('timestamp')) misp.applyProposals() misp.filterAttributes() misp.combineIdenticalAttributes() print(json.dumps(misp.getToAddAttributes())) print(json.dumps(misp.getToDeleteAttributes())) Attributes: conditions (list): List of available conditions in rules file. available_operators (list): List of available operators in rules file. available_fields (dict): Dictionary of available fields in rules file. with corresponding conditions and value type. attributes_to_add (list of dicts): List of attributes to create in receiving class. Each attribute is dict with key names from MISP. attributes_to_del (list of dicts): List of attributes to remove in receiving class. Each attribute is dict with key names from MISP. organisations (list of dicts): List of all available organisations with its corresponding priorities. pymisp (PyMISP): PyMISP object for handling communication with MISP. helper (Helper): Object with some helping methods for processing. misp_attributes (list of dicts): Attributes downloaded from MISP rules (): ... Args: url (str): MISP URL. key (str): MISP API key. verify_cert (bool): Check the validity of MISP certificate """ conditions = [ '=', '!=', '~', '!~', '[]', '![]', '>', '<', '>=', '<=', 'r', '!r' ] available_operators = ['&', '|'] available_fields = { # TODO - create separate dict with types and avaliable operators # TODO - there are the same conditions for every field with the same type # Attribute 'id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'event_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'object_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'category': { 'conditions': [ '=', '!=', '[]', '![]' ], 'value': ['Antivirus detection', 'Artifacts dropped', 'Attribution', 'External analysis', 'Financial fraud', 'Internal reference', 'Network activity', 'Other', 'Payload delivery', 'Payload installation', 'Payload type', 'Persistence mechanism', 'Person', 'Social network', 'Support Tool', 'Targeting data'], }, 'type': { 'conditions': [ '=', '!=', '[]', '![]' ], # ip type is only for domain|ip type 'value': ['ip', 'ip-src', 'ip-dst', 'email-src', 'email-dst', 'url', 'domain', 'hostname', 'email-subject', 'email-attachment', 'user-agent', 'target-email', 'filename', 'domain|ip', 'filename|md5', 'filename|sha1', 'filename|sha256'] }, 'to_ids': { 'conditions': [ '=', '!=' ], 'value': [True, False], }, 'uuid': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]', 'r', '!r' ], 'value': '[STR]', }, 'timestamp': { 'conditions': [ '=', '!=', '>', '<', '>=', '<=' ], 'value': '[TIME]', # '^[0-9]+(y|m|d|h)$' }, 'distribution': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'sharing_group_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'comment': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]', 'r', '!r' ], 'value': '[STR]', }, # TODO - deleted 'to_ids': { 'conditions': [ '=', '!=' ], 'value': [True, False], }, 'value': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]', 'r', '!r' ], 'value': '[STR]', }, # Event 'Event.id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.org_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.orgc_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.date': { 'conditions': [ '=', '!=', '>', '<', '>=', '<=' ], 'value': '[TIME]', # '^[0-9]+(y|m|d|h)$' }, 'Event.threat_level_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.info': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, 'Event.published': { 'conditions': [ '=', '!=' ], 'value': [True, False], }, 'Event.uuid': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, 'Event.analysis': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.timestamp': { 'conditions': [ '=', '!=', '>', '<', '>=', '<=' ], 'value': '[TIME]', # '^[0-9]+(y|m|d|h)$' }, 'Event.distribution': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.proposal_email_lock': { 'conditions': [ '=', '!=' ], 'value': [True, False], }, 'Event.locked': { 'conditions': [ '=', '!=' ], 'value': [True, False], }, 'Event.publish_timestamp': { 'conditions': [ '=', '!=', '>', '<', '>=', '<=' ], 'value': '[TIME]', # '^[0-9]+(y|m|d|h)$' }, 'Event.sharing_group_id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.disable_correlation': { 'conditions': [ '=', '!=' ], 'value': [True, False], }, # Event Org 'Event.Org.name': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, 'Event.Org.uuid': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, # Event Tag 'Event.Tag.id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.Tag.name': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, # Event Galaxy 'Event.Galaxy.id': { 'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='], 'value': '[INT]' }, 'Event.Galaxy.uuid': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, 'Event.Galaxy.name': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, 'Event.Galaxy.type': { 'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ], 'value': '[STR]' }, } attributes_to_add = [] attributes_to_del = [] organisations = [] def __init__(self, url, key, verify_cert): self.pymisp = PyMISP(url, key, verify_cert) self.helper = Helper() def loadRules(self, rules_file): """Load and verify rules from file. It accepts yaml and json files. Saves rules to self.rules Args: rules_file (str): Rules filename. Exits: If there is wrong file type or if there is an bug in the format of the file. """ # Rules file check if rules_file[-5:] == '.yaml': rules = yaml.load(open(rules_file, 'r')) elif rules_file[-5:] == '.json': rules = json.load(open(rules_file, 'r')) else: logging.error('Rules file in wrong format: {} insted of {}'.format(rules_file[-5:], ['yaml', 'json'])) exit() if not self.verifyRules(rules): logging.error('There is an error in the rules file: {}'.format(rules_file)) exit() self.rules = rules def downloadAttributes(self, timestamp): """Download attributes from MISP in a given timestamp. It enriches each attribute with data about event. Save attributes to self.misp_attributes. Sends one REST API request to MISP. Args: timestamp (str): Interval since last update (in seconds or 1d, 1h, ...). """ p = self.pymisp.search('attributes', timestamp=timestamp, deleted=True, includeProposals=True)['response'] if 'Attribute' not in p: logging.info('No new or changed attributes in MISP.') return self.misp_attributes = p['Attribute'] # Enrich attributes with Event info if len(self.misp_attributes) != 0: if 'threat_level_id' not in self.misp_attributes[0]['Event']: self.enrichAttributes() self.misp_attributes = self.explodePiped(self.misp_attributes) try: time_readable = datetime.utcfromtimestamp(int(timestamp)).strftime('%Y-%m-%d %H:%M:%S') except ValueError: time_readable = timestamp logging.info('Downloaded {} attributes from MISP since {}.'.format(len(self.misp_attributes), time_readable)) def loadAttributes(self, fname): """Load attributes from JSON file. Args: fname (str): JSON filename Exits: If extension of the file is not json. """ if fname[-5:] != '.json': logging.error('Input file in wrong format: {} insted of json'.format(priorities_file[-5:])) exit() self.misp_attributes = json.load(open(fname, 'r')) logging.info('Loaded {} attributes from MISP.'.format(len(self.misp_attributes))) def enrichAttributes(self): """Pull additional data about events and enrich attributes with that Modify self.misp_attributes and saves result there. Sends one REST API request to MISP. """ event_ids = {} for attribute in self.misp_attributes: event_ids[int(attribute['Event']['id'])] = True event_ids = list(event_ids.keys()) events = self.pymisp.search('events', eventid=event_ids)['response'] for i in range(len(events)): del events[i]['Event']['Attribute'] del events[i]['Event']['ShadowAttribute'] del events[i]['Event']['RelatedEvent'] for attribute in self.misp_attributes: for event in events: event = event['Event'] if event['id'] == attribute['event_id']: break attribute['Event'] = event def printAttributes(self): """Print downloaded attributes in json format""" print(json.dumps(self.misp_attributes)) def downloadOrganisations(self): """Download all organisations Saves result to self.organisations """ organisations = self.pymisp.get_organisations_list('all')['response'] # Get max organisation id max_id = 0 for org in organisations: id = int(org['Organisation']['id']) if id > max_id: max_id = id self.organisations = [None] * (max_id + 1) for org in organisations: o = org['Organisation'] self.organisations[int(o['id'])] = {'name': o['name']} logging.info('Loaded {} organisations from MISP.'.format(len(organisations))) def loadPriorities(self, priorities_file): """Load and verify priorities from file Accept yaml and json files. Save priorities to self.organisations. Args: priorities_file (str): Priorities filename. Exits: If there is wrong file type or if there is an bug in the format of the file. """ # Priorities file check if priorities_file[-5:] == '.yaml': priorities = yaml.load(open(priorities_file, 'r')) elif priorities_file[-5:] == '.json': priorities = json.load(open(priorities_file, 'r')) else: logging.error('Priorities file in wrong format: {} insted of {}'.format(priorities_file[-5:], ['yaml', 'json'])) exit() verified = self.verifyPriorities(priorities) if not verified: logging.error('There is an error in the priorities file: {}'.format(priorities_file)) exit() loaded = 0 for org in verified: for id in range(len(self.organisations)): if self.organisations[id] is not None and org['name'] == self.organisations[id]['name']: loaded += 1 self.organisations[id]['priority'] = org['priority'] logging.info('Loaded {} priorities.'.format(loaded)) def explodePiped(self, misp_attributes): """Explode each piped attribute into two It uses deepcopy to completely copy attribute with. Args: misp_attributes (list of dicts): List of attributes Returns: list: List of attributes """ piped = [] for misp_attribute in misp_attributes: if '|' in misp_attribute['type']: attribute1 = copy.deepcopy(misp_attribute) attribute1['type'] = attribute1['type'].split('|')[0] attribute1['value'] = attribute1['value'].split('|')[0] attribute2 = copy.deepcopy(misp_attribute) attribute2['type'] = attribute2['type'].split('|')[1] attribute2['value'] = attribute2['value'].split('|')[1] piped.append(attribute1) piped.append(attribute2) else: piped.append(misp_attribute) return piped def applyProposals(self): """Apply proposals to attributes for further processing. Use priorities to decide which proposal is more important. Operates on self.misp_attributes Saves result to self.attributes_to_add Exits: If no organisations have been loaded. """ if self.misp_attributes is None: logging.warning('No MISP attributes to parse') return if self.organisations is None: logging.error('No organisations loaded') exit() merged = [] count = 0 for attribute in self.misp_attributes: if 'ShadowAttribute' not in attribute: merged.append(attribute) continue final = attribute event = attribute['Event'] for shadow in attribute['ShadowAttribute']: # Compare orgs priorities final_priority = self.organisations[int(event['orgc_id'])].get('priority', 0) shadow_priority = self.organisations[int(shadow['org_id'])].get('priority', 0) if shadow_priority > final_priority: final = shadow final['Event'] = event continue elif final_priority > shadow_priority: continue # Compare ids flags final_ids = final['to_ids'] final_delete = final['deleted'] final_decision = not final_delete if final_delete else final_ids shadow_ids = shadow['to_ids'] shadow_delete = shadow['proposal_to_delete'] shadow_decision = not shadow_delete if shadow_delete else shadow_ids # When two shadow attributes are with different decision take this one where decision is positive # old_id key exists only in final which is shadow attribute if 'old_id' in final and final_decision != shadow_decision and final_decision: continue # By default replace final attribute with the newest shadow attribute final = shadow final['Event'] = event merged.append(final) if 'old_id' in final: count += 1 self.attributes_to_add = merged # self.merged_attributes = merged logging.info('{} proposals applied to attributes.'.format(count)) def filterAttributes(self): """Filter attributes according to rules Operates on self.attributes_to_add Saves result to self.attributes_to_add Exits: If there is no rules loaded. """ if self.attributes_to_add is None: logging.warning('No MISP attributes to parse') return if self.rules is None: logging.error('No rules found') exit() filtered = [] for attribute in self.attributes_to_add: if self.checkAttribute(attribute, self.rules): filtered.append(attribute) else: self.attributes_to_del.append(attribute) logging.info('With total {} attributes {} marked to add, {} marked to delete according to rules.'.format(len(self.attributes_to_add), len(filtered), len(self.attributes_to_del))) self.attributes_to_add = filtered #logging.info('{} out of {} attributes filtered out according to rules.'.format(len(self.merged_attributes) - len(self.attributes_to_add), len(self.merged_attributes))) def combineIdenticalAttributes(self): """Combine attributes with identical values from different events. Operates on self.attributes_to_add Todo: Combine identical to_delete (not only to_add) """ combined = [] # Search for attrs with identical value and merge them by adding dict key 'Related' to first of them for left in self.attributes_to_add: if 'already_parsed' in left: continue related = [] for right in self.attributes_to_add: if left['value'] == right['value']: if left['uuid'] == right['uuid']: continue related.append(copy.deepcopy(right)) right['already_parsed'] = 1 tmp = copy.deepcopy(left) if len(related) > 0: tmp['Related'] = related combined.append(tmp) logging.info('Found {} identical attributes. After combine {} attributes left.'.format(len(self.attributes_to_add) - len(combined), len(combined))) # self.combined_attributes = combined self.attributes_to_add = combined def skipUnpublished(self): """Remove from attributes_to_add and attributes_to_delete attributes from unpublished events""" skipped = 0 for i in range(len(self.attributes_to_add)): if not self.checkIfPublished(self.attributes_to_add[i]): del self.attributes_to_add[i] skipped += 1 for i in range(len(self.attributes_to_del)): if not self.checkIfPublished(self.attributes_to_del[i]): del self.attributes_to_del[i] skipped += 1 logging.info('Skipped {} attributes from unpublished events.'.format(skipped)) def checkIfPublished(self, attribute): """Check if attribute's event is published Args: attribute (dict): Attribute dictionary with all the fields from MISP. Returns: bool: True if event of a given attribute is published. False otherwise. """ if 'Event' not in attribute: logging.error('Attribute {} from event {} does not have event details. Published state cannot be established.'.format(attribute['value'], attribute['event_id'])) exit() if 'published' not in attribute['Event']: logging.error('Attribute {} from event {} does not have publish state.'.format(attribute['value'], attribute['event_id'])) exit() return attribute['Event']['published'] def getToAddAttributes(self): """Return attributes which should be added""" return self.attributes_to_add def getToDeleteAttributes(self): """Return attributes which should be removed""" return self.attributes_to_del def checkAttribute(self, attribute, rules): """Recursively check if attribute passes rules filter or not Args: attribute (dict): Attribute dictionary with all the fields from MISP. rules (nested dict): Rules dictionary. Returns: bool: True if attribute passes filter. False otherwise. Exits: If rules dictionary is incorrectly build (e.g. no operator, or condition is incorrect) """ components = rules.get('components') operator = rules.get('operator') if operator is None: logging.error('Error in rules file. Rules doesn\'t have operator field.') exit() if components is None: logging.error('Error in rules file. Rules doesn\'t have components field.') exit() if not isinstance(components, list): logging.error('Error in rules file. Components field is type {} insted of a list.'.format(type(components))) exit() if operator not in self.available_operators: logging.error('Error in rules file. Operator {} is not allowed operator. Allowed operators are: {}'.format(operator, self.available_operators)) exit() # Set first left side of the condition if operator == '&': result = True else: result = False for c in components: if 'field' in c and 'condition' in c and 'value' in c: if '.' in c['field']: attribute_values = self.helper.getValueFromObject(attribute, c['field']) else: attribute_values = attribute[c['field']] if not isinstance(attribute_values, list): attribute_values = [attribute_values] if len(attribute_values) == 0: cond_result = False else: # Check if any of the values meet the condition for attribute_value in attribute_values: # Analyzing one condition if c['condition'] == '=': cond_result = attribute_value == c['value'] elif c['condition'] == '!=': cond_result = attribute_value != c['value'] elif c['condition'] == '~': cond_result = c['value'] in attribute_value elif c['condition'] == '!~': cond_result = c['value'] not in attribute_value elif c['condition'] == '[]': cond_result = attribute_value in c['value'] elif c['condition'] == '![]': cond_result = attribute_value not in c['value'] elif c['condition'] == '>': cond_result = int(attribute_value) > int(c['value']) elif c['condition'] == '<': cond_result = int(attribute_value) < int(c['value']) elif c['condition'] == '>=': cond_result = int(attribute_value) >= int(c['value']) elif c['condition'] == '<=': cond_result = int(attribute_value) <= int(c['value']) elif c['condition'] == 'r': regex = c['value'] cond_result = re.search(regex, attribute_value) is not None elif c['condition'] == '!r': regex = c['value'] cond_result = re.search(regex, attribute_value) is None else: logging.error('Error in checking attribute {}'.format(attribute_value)) exit() if cond_result: break elif 'components' in c and 'operator' in c: # Going down to deeper conditions cond_result = self.checkAttribute(attribute, c) else: logging.warning('Component has invalid structure. It has to have fields: "field", "value", "condition" or "components", "operator". Instead it has: {}'.format(', '.join(c.keys()))) continue if operator == '&': result = result and cond_result else: result = result or cond_result return result # def extendAttributes(self): # """Extends attributes with additional fields.""" # for num in range(len(self.attributes_to_add)): # a = self.extendAttribute(self.attributes_to_add[num]) # self.attributes_to_add[num] = a # def extendAttribute(self, attribute): # """Extends attribute with additional fields ingested by ArcSight.""" # # 1. Organisation name # org_id = int(attribute['Event']['org_id']) # orgc_id = int(attribute['Event']['orgc_id']) # attribute['Event']['org_name'] = self.organisations[org_id]['name'] # attribute['Event']['orgc_name'] = self.organisations[orgc_id]['name'] # return attribute def verifyPriorities(self, priorities): """Verify correctness of priorities Verify all provided priorities. Skip organisation if there is some error. Args: priorities (dict): Priorities to verify. Example {'organisations': [ {'name': 'Organisation A', 'priority': 1}, {'name': 'Organisation B', 'priority': 2} ]}: Returns: bool: False if there is no organisations key in dictionary. list: List of parsed priorities. """ org_priorities = priorities.get('organisations') if org_priorities is None: logging.warning('Error in priorities file. Organisations field is required.') return False verified = [] for priority_org in org_priorities: if 'name' not in priority_org: logging.warning('Error in priorities file. Organisation doesn\'t have name field. Skipping.') continue else: # TODO Write verification if this prioritzanisation exists in MISP orgs = [org['name'] for org in self.organisations if org is not None] if priority_org['name'] not in orgs: logging.warning('Error in priorities file. Organisation {} doesn\'t exist. Skipping.'.format(priority_org.get('name'))) continue if 'priority' not in priority_org: logging.warning('Error in priorities file. Organisation {} doesn\'t have priority field. Skipping.'.format(priority_org.get('name'))) continue elif not isinstance(priority_org.get('priority'), int): logging.warning('Error in priorities file. Organisation {} priority is not int. It is: {}. Skipping.'.format(priority_org.get('name'), type(priority_org.get('priority')))) continue verified.append(priority_org) return verified def verifyRules(self, rules): """Verify correctness of rules Verify recursively all provided operators, fields, conditions and values. Args: rules (nested dict): Rules to verify: operator: "&" components: - field: Event.Tag.name condition: "=" value: "APT" Returns: bool: True if successfully parsed rules, False otherwise. """ components = rules.get('components') operator = rules.get('operator') if operator is None: logging.warning('Error in rules file. Rules doesn\'t have operator field.') return False if components is None: logging.warning('Error in rules file. Rules doesn\'t have components field.') return False if not isinstance(components, list): logging.warning('Error in rules file. Components field is type "{}" insted of a list.'.format(type(components))) return False if operator not in self.available_operators: logging.warning('Error in rules file. Operator "{}" is not allowed operator. Allowed operators are: {}'.format(operator, self.available_operators)) return False result = True for c in components: if 'field' in c and 'value' in c and 'condition' in c: if c['condition'] not in self.conditions: logging.warning('Condition "{}" is invalid for the field "{}". All posible conditions: {}'.format(c['condition'], c['field'], ', '.join(self.conditions))) result = False # Check if field is correct if c['field'] not in self.available_fields: logging.warning('Field "{}" is invalid. Valid fields are: {}'.format(c['field'], ', '.join(self.available_fields.keys()))) result = False # Check if condition is appropriate for the field elif c['condition'] not in self.available_fields[c['field']]['conditions']: logging.warning('Field "{}" cannot be processed with condition "{}". Available conditions: {}'.format(c['field'], c['condition'], ', '.join(self.available_fields[c['field']]['conditions']))) result = False # Check if value is appropriate type for the field else: if self.available_fields[c['field']]['value'] == '[INT]': if isinstance(c['value'], list): for temp_value in c['value']: if not isinstance(temp_value, int): logging.warning('Field "{}" can have only int values. One of the value "{}" is type "{}".'.format(c['field'], temp_value, type(temp_value))) result = False elif not isinstance(c['value'], int): logging.warning('Field "{}" can have only int values. Value "{}" is type "{}".'.format(c['field'], c['value'], type(c['value']))) result = False elif self.available_fields[c['field']]['value'] == '[STR]': if isinstance(c['value'], list): for temp_value in c['value']: if not isinstance(temp_value, str): logging.warning('Field "{}" can have only str values. One of the value "{}" is type "{}".'.format(c['field'], temp_value, type(temp_value))) result = False elif not isinstance(c['value'], (str, list)): logging.warning('Field "{}" can have only str values. Value "{}" is type "{}".'.format(c['field'], c['value'], type(c['value']))) result = False elif self.available_fields[c['field']]['value'] == '[TIME]': search = re.search(r'^[0-9]+(y|m|d|h)?$', c['value'], re.I) if not search: logging.warning('Field "{}" can have only time values (1d, 3h, 2m). Value "{}" is type "{}".'.format(c['field'], c['value'], type(c['value']))) result = False elif isinstance(self.available_fields[c['field']]['value'], list): if isinstance(c['value'], list): for temp_value in c['value']: if temp_value not in self.available_fields[c['field']]['value']: logging.warning('One of the values "{}" is invalid for the field "{}". It has to be one of the following: {}'.format(temp_value, c['field'], ', '.join([str(x) for x in self.available_fields[c['field']]['value']]))) result = False elif c['value'] not in self.available_fields[c['field']]['value']: logging.warning('Value "{}" is invalid for the field "{}". It has to be one of the following: {}'.format(c['value'], c['field'], ', '.join([str(x) for x in self.available_fields[c['field']]['value']]))) result = False elif 'components' in c and 'operator' in c: # Going down to deeper conditions result = result and self.verifyRules(c) else: logging.warning('Component has invalid structure. It has to have fields: "field", "value", "condition" or "components", "operator". Instead it has: {}'.format(', '.join(c.keys()))) result = False return result
class ArcSight: """Update ArcSight active lists with provided data. Take data from MISP and push it to ArcSight. After updating active lists it pulls data from current Active Lists and make diff between those entries and entries in memory (from MISP or other source). Example: arcsight = ArcSight(config.get('arcsight-url'), config.get('arcsight-username'), config.get('arcsight-password'), config.get('arcsight-map'), proxies=proxy) arcsight.addEntries(to_add) arcsight.deleteEntries(to_delete) if arcsight.verifyChanges() is not False: print('MISP to ArcSight export went successful. Expected result achieved.') Attributes: test (bool): If true don't update active lists just print actions. helper (Helper): Object with some helping methods for processing. maps (dict): Mapping rules of active lists. default_merge_function (str): Default function used to merge multiple values. default_convert_function (str): Default function used to conver mapped values. active_lists (ActiveLists): pyasesm.ActiveLists object for handling communication with ArcSight Active Lists. entries (dict of lists): Dictionary with current active lists entries. to_add (dict of lists): Dictionary with active lists entries to add. to_delete (dict of lists): Dictionary with active lists entries to remove. attributes_to_skip (list of dicts): List of attributes to skip. Those attributes will not change their state. They will not be added to active list or removed/updated from one. Args: url (str): ArcSight ESM URL. username (str): ArcSight ESM username. password (str): ArcSight ESM password. map_file (str): Filename of yaml file with mapping rules. proxies (dict): Proxy configuration verify (bool): Check the validity of ArcSight ESM certificate Exits: If there is wrong map_file type or there was an error in ESM connection. """ def __init__(self, url, username, password, map_file, proxies=None, verify=True): # Ruleset to map fields from MISP to ArcSight # For each field of MISP attribute convert function (example: self._convertComma) will be executed # If dots are in misp field name threat it as a nested dict. Example A.B.C -> misp_entry[A][B][C] self.test = False self.helper = Helper() self.maps = yaml.load(open(map_file, 'r')) if not self._verifyMap(self.maps): logging.error('There is an error in the map file: {}'.format(map_file)) exit() self.default_merge_function = 'space' self.default_convert_function = 'return' self.active_lists = ActiveLists(url, username, password, proxies=proxies, verify=False) if not self.active_lists: logging.error('Could not connect to ArcSight ESM') exit() self.entries = {} self.to_add = {} self.to_delete = {} self.attributes_to_skip = [] for i in range(len(self.maps)): active_list = self.maps[i] # Select active list primary key if there is none if 'primary_key' not in active_list: for rule in active_list['map']: if rule['misp'] == 'value': self.maps[i]['primary_key'] = rule['arcsight'] break self.to_add[active_list['id']] = [] self.to_delete[active_list['id']] = [] self.entries[active_list['id']] = self.active_lists.getEntries(active_list['id']) def skipAttributes(self, attributes): """Saves attributes to skip. Args: attributes (list of dicts): MISP attributes to skip """ for attribute in attributes: self.attributes_to_skip.append(attribute['value']) def addEntries(self, to_add): """Add list of entries to arcsight. Args: to_add (list): List of entries. Returns: int: Number of added entries. """ for new in to_add: self.prepareEntryToAdd(new) result = 0 for list_id in self.to_add: if len(self.to_add[list_id]) > 0: result += self.active_lists.addEntries(self.to_add[list_id], list_id) return result def prepareEntryToAdd(self, misp_attribute): """Prepare entry by mapping it from MISP field names to Active List. Save result to self.to_add Args: misp_attribute (dict): MISP attribute with all the properties for preparation to add. Todo: Comment on for's what are they for. """ assigned_num = 0 for active_list in self.maps: arcsight_entry = {} if isinstance(active_list['value'], list): if misp_attribute.get(active_list['key']) in active_list['value']: logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name'])) assigned_num += 1 else: continue else: if misp_attribute.get(active_list['key']) == active_list['value']: logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name'])) assigned_num += 1 else: continue for rule in active_list['map']: # Iterate through rules # Prepare function for converting MISP value for ArcSight convert_name = rule.get('convert', self.default_convert_function) convert_name = convert_name[0].upper() + convert_name[1:] convertFunction = getattr(self, '_convert'+convert_name) merge_name = rule.get('merge', self.default_merge_function) merge_name = merge_name[0].upper() + merge_name[1:] mergeFunction = getattr(self, '_merge'+merge_name) if isinstance(rule['misp'], list): map_misp_fields = rule['misp'] else: # Create one-element list for easier and more clear processing map_misp_fields = [rule['misp']] misp_values = [] # 1. Iterate through all fields in MISP and merge them for dot_filter in map_misp_fields: # if dot_filter[len(dot_filter)-1] != '.': # # Add dot at the end for easier and more clear processing # dot_filter += '.' # 2. Iterate through dotted field names and extract nested falue (example: misp_attribute['Event']['info']) misp_values.append(self.helper.getValueFromObject(misp_attribute, dot_filter, convertFunction, mergeFunction)) arcsight_entry[rule['arcsight']] = mergeFunction(misp_values) self.to_add[active_list['id']].append(arcsight_entry) logging.debug('Attribute {} assigned to {} lists.'.format(misp_attribute['value'].replace("\r\n",' '), assigned_num)) def deleteEntries(self, to_delete): """Remove list of entries to arcsight. Args: to_add (list): List of entries. Returns: int: Number of removed entries """ for entry in to_delete: self.prepareEntryToDelete(entry) result = 0 for list_id in self.to_delete: if len(self.to_delete[list_id]) > 0: result += self.active_lists.deleteEntries(self.to_delete[list_id], list_id) return result def prepareEntryToDelete(self, misp_attribute): """Prepare entry by mapping it from MISP field names to Active List. Save result to self.to_delete. Args: misp_attribute (dict): MISP attribute with all the properties for preparation to delete. Todo: verify correctness of this function. Last line of this method is incorrect (logging.debug) """ assigned_num = 0 for active_list in self.maps: if isinstance(active_list['value'], list): if misp_attribute.get(active_list['key']) in active_list['value']: logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name'])) assigned_num += 1 else: continue else: if misp_attribute.get(active_list['key']) == active_list['value']: logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name'])) assigned_num += 1 else: continue for entry in self.entries[active_list['id']]: if entry[active_list['primary_key']] == misp_attribute['value']: self.to_delete[active_list['id']].append(entry) logging.debug('Attribute {} assigned to {} lists.'.format(misp_attribute['value'].replace("\r\n",' '), assigned_num)) def testDifferences(self, to_add, to_delete): """Prepare lists of attributes to add and to delete. Additionaly save the different jsons to logs directory. Args: to_add (list): List of entries to add to_delete (list): List of entries to delete Returns: bool: True if adding and removing entries from active lists went successful. """ self.test = True for entry in to_add: self.prepareEntryToAdd(entry) for entry in to_delete: self.prepareEntryToDelete(entry) result = self.verifyChanges() if result is True: return True # Prints warning messages - differences for error in result: print('List: {}. Attribute: {} = {}. {}'.format(error['list'], error['primary_key'], error['value'], error['message'])) # Save differences now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') with open('logs/{}_current.json'.format(now), 'w') as file: json.dump(self.entries, file) with open('logs/{}_expected.json'.format(now), 'w') as file: json.dump(self.expected, file) return False def selectActiveList(self, misp_attribute): """Get num and id of active list for particular misp attribute Args: misp_attribute (dict): MISP attribute with all the properties. Returns: dict: List number and list id of a given misp attribute. """ list_num = list_id = None for i in range(len(self.maps)): list_map = self.maps[i] if isinstance(list_map['value'], list): if misp_attribute.get(list_map['key']) in list_map['value']: list_num = i list_id = self.maps[i]['id'] logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'], list_map['name'])) break else: if misp_attribute.get(list_map['key']) == list_map['value']: list_num = i list_id = self.maps[i]['id'] logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'], list_map['name'])) break if list_num is None: return False return {'num': list_num, 'id': list_id} def info(self, list_id=None): """Get the details of active list Args: list_id (int): Id of active list. Returns: dict: Active list properties. """ return self.active_lists.info(list_id) def verifyChanges(self): """Pull entries from ArcSight again and compare it from those before the change Returns: bool: True if lists are equal and export went successfully. List: List with differencies. """ # Prepare list with expected ArcSight entries self.expected = {} for active_list in self.maps: list_id = active_list['id'] primary_key = active_list['primary_key'] self.expected[list_id] = copy.deepcopy(self.entries[list_id]) if len(self.to_add[list_id]) == 0: continue for to_add_entry in self.to_add[list_id]: exists = False for expected_entry in self.expected[list_id]: if to_add_entry[primary_key] == expected_entry[primary_key]: exists = True break if not exists: self.expected[list_id].append(to_add_entry) # for entry in self.entries[list_id]: # exists = False # for to_add_entry in self.to_add[list_id]: # if to_add_entry[primary_key] == entry[primary_key]: # exists = True # self.expected[list_id].append(to_add_entry) # break # if not exists: # self.expected[list_id].append(entry) # print(json.dumps(self.to_add)) # exit() new_expected = [] for expected_entry in self.expected[list_id]: add = True for to_delete_entry in self.to_delete[list_id]: if to_delete_entry[primary_key] == expected_entry[primary_key]: add = False break if add: new_expected.append(expected_entry) self.expected[list_id] = new_expected del new_expected valid_str = '_valid_arcsight_entry' errors = [] for active_list in self.maps: list_id = active_list['id'] primary_key = active_list['primary_key'] if self.test: current = self.entries[list_id] else: current = self.active_lists.getEntries(list_id) # Compare precisely current ArcSight database and expected result for expected_entry in self.expected[list_id]: found = False current_entry = None for i in range(len(current)): current_entry = current[i] if expected_entry[primary_key] != current_entry[primary_key]: continue found = True current_entry[valid_str] = True errors.extend(self._compareEntries(current_entry, expected_entry, primary_key, active_list['name'])) break if not found: # if current_entry is None or valid_str not in current_entry: errors.append({'primary_key': primary_key, 'value': expected_entry[primary_key], 'list': active_list['name'], 'message': 'Entry does not exist in ArcSight.'}) if not self.test: logging.error('ArcSight entry {} does not exist in list {}.'.format(expected_entry[primary_key], active_list['name'])) # Check if there are some current entries which are not in the expected list for current_entry in current: if valid_str not in current_entry: errors.append({'primary_key': primary_key, 'value': current_entry[primary_key], 'list': active_list['name'], 'message': 'Entry should not exists in ArcSight'}) if not self.test: logging.error('Entry {} should not exist in ArcSight {}.'.format(current_entry[primary_key], active_list['name'])) if errors: return errors return True def _compareEntries(self, current, expected, primary_key, list_name): """Compares current and expected ArcSight entry Args: current (dict): Current entry from active list. expected (dict): Entry which is expected to be on active list. primary_key (str): Primary key based on which comparison is made. list_name (str): List name for messaging purposes. Returns: list: Errors if any or empty list. """ errors = [] for key, expected_value in expected.items(): # This is temporary bypass for auto-updating time in ticketDate. # Remove it when GMT to timestamp conversion is ready and # addEntries is modified so it sends only changed entries if key in ('ticketDate', 'publishedDate'): continue if key not in current: errors.append({'primary_key': primary_key, 'value': expected[primary_key], 'list': list_name, 'message': 'missing key: '+key, }) if not self.test: logging.error('ArcSight entry {} was not updated in list {}. {} does not exist in ArcSight and should be "{}".'.format(expected[primary_key], list_name, key, expected_value)) else: if current[key] != expected_value: errors.append({'primary_key': primary_key, 'value': expected[primary_key], 'list': list_name, 'message': 'value is different than expected for key: {}. Current: {}. Expected: {}.'.format(key, current[key], expected_value), }) if not self.test: logging.error('ArcSight entry {} was not updated in list {}. {} is "{}" but should be "{}".'.format(expected[primary_key], list_name, key, current[key], expected_value)) return errors def _mergeComma(self, values): """Merge values by comma Args: values (list/any): list of values to merge. Returns: str: Merged values or original value if not list was provided. """ if isinstance(values, list): return ','.join(list(filter(None, values))) return values def _mergeSpace(self, values): """Merge values by space Args: values (list/any): list of values to merge. Returns: str: Merged values or original value if not list was provided. """ if isinstance(values, list): return ' '.join(list(filter(None, values))) return values def _mergeDash(self, values): """Merge values by dash Args: values (list/any): list of values to merge. Returns: str: Merged values or original value if not list was provided. """ if isinstance(values, list): return '-'.join(list(filter(None, values))) return values def _convertReturn(self, value): """Return the same value. This method is required to keep the code clean. Args: values (any): Value to return. Returns: any: Not modified value. """ return value def _convertTimestampToEpoch(self, timestamps): """Convert timestamp to epoch timestamp Args: timestamps (list/str): List of timestamps or one timestamp in int. Returns: str: Merged with comma string with epoch format of given timestaps. """ if isinstance(timestamps, list): return self._mergeComma([timestamp+'000' for timestamp in timestamps]) return timestamps+'000' def _convertDateToEpoch(self, dates): """Convert dates to epoch timestamp Args: dates (list/str): Dates in format Y-m-d or list of dates Returns: str: Merged with comma string with epoch format of given dates. """ if isinstance(dates, list): # convert all dates in list to epoch and merge with commas return self._mergeComma([str(int(time.mktime(time.strptime(str(date), '%Y-%m-%d'))))+'000' for date in dates]) date = dates return str(int(time.mktime(time.strptime(str(date), '%Y-%m-%d'))))+'000' def _convertIpToClassC(self, ips): """Convert ip to class C with just first three octets Args: ips (list/str): Ip addess or list of ip addresses. Returns: str: First three octets of each ip merged with comma. """ if isinstance(ips, list): return self._mergeComma([ip[ip.rfind('.')] for ip in ips]) return ips[:ips.rfind('.')] def _find(self, value, list_id): """Find attributes in the current active list based on the value Args: value (str): Value to search for. list_id (int): Id of active list where the search is done. Returns: bool: True if found, false otherwise. """ # Select active list primary key for active_list in self.maps: if active_list['id'] != list_id: continue primary_key = active_list['primary_key'] break for entry in self.entries[list_id]: if entry[primary_key] == value: #return entry return True return False def _verifyMap(self, map_list): """Verify map file content correctness Args: map_list (dict): Map Returns: bool: True if map has all required fields. False otherwise. Todo: Code it. Right now it's doing nothing. """ if map_list: return True return False
def buy(self): """Buy upgrades for both energy and magic. Requires the confirmation popup button for EXP purchases in settings to be turned OFF. This uses all available exp, so use with caution. """ if self.ecap < 10000 or self.ecap % 250 != 0: print("Ecap value not divisible by 250 or lower than 10000, not" + " spending exp.") return if self.mcap < 10000 or self.mcap % 250 != 0: print("Mcap value not divisible by 250 or lower than 10000, not" + " spending exp.") return Stats.set_value_with_ocr("XP") if Stats.OCR_failed: print('OCR failed, exiting upgrade routine.') return current_exp = Stats.xp e_cost = coords.EPOWER_COST + coords.ECAP_COST * self.ecap + ( coords.EBAR_COST * self.ebar) m_cost = coords.MPOWER_COST + coords.MCAP_COST * self.mcap + ( coords.MBAR_COST * self.mbar) total_price = m_cost + self.e2m_ratio * e_cost # Skip upgrading if we don't have enough exp to buy at least one # complete set of upgrades, in order to maintain our perfect ratios :) if total_price > current_exp: if self.report: print("No XP Upgrade :{:^8} of {:^8}".format( Helper.human_format(current_exp), Helper.human_format(total_price))) return amount = int(current_exp // total_price) e_power = amount * self.e2m_ratio e_cap = amount * self.ecap * self.e2m_ratio e_bars = amount * self.ebar * self.e2m_ratio m_power = amount m_cap = amount * self.mcap m_bars = amount * self.mbar Navigation.exp() Inputs.click(*coords.EM_POW_BOX) Inputs.send_string(str(e_power)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_CAP_BOX) Inputs.send_string(str(e_cap)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_BAR_BOX) Inputs.send_string(str(e_bars)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_POW_BUY) Inputs.click(*coords.EM_CAP_BUY) Inputs.click(*coords.EM_BAR_BUY) Navigation.exp_magic() Inputs.click(*coords.EM_POW_BOX) Inputs.send_string(str(m_power)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_CAP_BOX) Inputs.send_string(str(m_cap)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_BAR_BOX) Inputs.send_string(str(m_bars)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_POW_BUY) Inputs.click(*coords.EM_CAP_BUY) Inputs.click(*coords.EM_BAR_BUY) Stats.set_value_with_ocr("XP") total_spent = coords.EPOWER_COST * e_power + coords.ECAP_COST * e_cap + coords.EBAR_COST * e_bars total_spent += coords.MPOWER_COST * m_power + coords.MCAP_COST * m_cap + coords.MBAR_COST * m_bars if self.report: print("Spent XP:{:^8}".format(Helper.human_format(total_spent))) print( "Energy | Pow:{:^8}{:^3}Cap:{:^8}{:^3}Bar:{:^8}{:^3}Magic | Pow:{:^8}{:^3}Cap:{:^8}{:^3}Bar:{:^8}" .format(Helper.human_format(e_power), "|", Helper.human_format(e_cap), "|", Helper.human_format(e_bars), "|", Helper.human_format(m_power), "|", Helper.human_format(m_cap), "|", Helper.human_format(m_bars)))
def buy(self): """Buy upgrades for hack energy Requires the confirmation popup button for EXP purchases in settings to be turned OFF. This uses all available exp, so use with caution. """ if (self.hcap < 10000 or self.hcap % 250 != 0) and self.hcap != 0: print("Ecap value not divisible by 250 or lower than 10000, not" + " spending exp.") return Stats.set_value_with_ocr("XP") if Stats.OCR_failed: print('OCR failed, exiting upgrade routine.') return current_exp = Stats.xp total_price = coords.HPOWER_COST * self.hpower + coords.HCAP_COST * self.hcap + coords.HBAR_COST * self.hbar # Skip upgrading if we don't have enough exp to buy at least one # complete set of upgrades, in order to maintain our perfect ratios :) if total_price > current_exp: if self.report: print("No XP Upgrade :{:^8} of {:^8}".format( Helper.human_format(current_exp), Helper.human_format(total_price))) return amount = int(current_exp // total_price) h_power = amount * self.hpower h_cap = amount * self.hcap h_bars = amount * self.hbar Navigation.exp_hack() Inputs.click(*coords.EM_POW_BOX) Inputs.send_string(str(h_power)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_CAP_BOX) Inputs.send_string(str(h_cap)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_BAR_BOX) Inputs.send_string(str(h_bars)) time.sleep(userset.MEDIUM_SLEEP) if h_power > 0: Inputs.click(*coords.EM_POW_BUY) if h_cap > 0: Inputs.click(*coords.EM_CAP_BUY) if h_bars > 0: Inputs.click(*coords.EM_BAR_BUY) Stats.set_value_with_ocr("XP") total_spent = coords.HPOWER_COST * h_power + coords.HCAP_COST * h_cap + coords.HBAR_COST * h_bars if self.report: print("Spent XP:{:^8}".format(Helper.human_format(total_spent))) print("New | Pow:{:^8}{:^3}Cap:{:^8}{:^3}Bar:{:^8}".format( Helper.human_format(h_power), "|", Helper.human_format(h_cap), "|", Helper.human_format(h_bars)))
def buy(self): """Buy upgrades for power, toughness, health and regen Requires the confirmation popup button for EXP purchases in settings to be turned OFF. This uses all available exp, so use with caution. """ Stats.set_value_with_ocr("XP") if Stats.OCR_failed: print('OCR failed, exiting upgrade routine.') return current_exp = Stats.xp total_price = (coords.APOWER_COST * self.power * self.ratio) total_price += (coords.ATOUGHNESS_COST * self.toughness * self.ratio) total_price += (coords.AHEALTH_COST * self.health * 10) total_price += math.floor(coords.AREGEN_COST * self.regen / 10) # Skip upgrading if we don't have enough exp to buy at least one # complete set of upgrades, in order to maintain our perfect ratios :) if total_price > current_exp: if self.report: print("No XP Upgrade :{:^8} of {:^8}".format( Helper.human_format(current_exp), Helper.human_format(total_price))) return amount = int(current_exp // total_price) a_power = amount * self.ratio a_toughness = amount * self.ratio a_health = amount * 10 a_regen = math.floor(amount / 10) if a_regen < 1: a_regen = 1.0 Navigation.exp_adventure() Inputs.click(*coords.EM_ADV_BOX) Inputs.send_string(str(a_power)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_POW_BOX) Inputs.send_string(str(a_toughness)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_CAP_BOX) Inputs.send_string(str(a_health)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_BAR_BOX) Inputs.send_string(str(a_regen)) time.sleep(userset.MEDIUM_SLEEP) Inputs.click(*coords.EM_ADV_BUT) Inputs.click(*coords.EM_POW_BUY) Inputs.click(*coords.EM_CAP_BUY) Inputs.click(*coords.EM_BAR_BUY) Stats.set_value_with_ocr("XP") total_spent = coords.APOWER_COST * a_power total_spent += coords.ATOUGHNESS_COST * a_toughness total_spent += coords.AHEALTH_COST * a_health total_spent += coords.AREGEN_COST * a_regen if self.report: print("Spent XP:{:^8}".format(Helper.human_format(total_spent))) print( "Power:{:^8}{:^3} Defense:{:^8}{:^3} Health:{:^8}{:^3} Regen:{:^8}" .format(Helper.human_format(a_power), "|", Helper.human_format(a_toughness), "|", Helper.human_format(a_health), "|", Helper.human_format(a_regen)))
from classes.helper import Helper from classes.inputs import Inputs Helper.init() Inputs.save_screenshot()
"""Snipe without Mega Buff.""" import time # Helper classes from classes.features import Adventure, MoneyPit from classes.helper import Helper Helper.init(True) while True: # main loop Adventure.snipe(0, 10, manual=True, bosses=True, highest=True) Adventure.adventure(0) # go wait at safe zone MoneyPit.pit() MoneyPit.spin() time.sleep(10)
class HelperTest( unittest.TestCase ): @staticmethod def buildSuite(): suite = unittest.TestSuite() suite.addTest(HelperTest("testShouldReturnInt")) suite.addTest(HelperTest("testShouldReturnDeltaX")) suite.addTest(HelperTest("testShouldReturnDeltaY")) suite.addTest(HelperTest("testShouldReturnRandomColor")) suite.addTest(HelperTest("testShouldCollide")) suite.addTest(HelperTest("testShouldNotCollide")) suite.addTest(HelperTest("testShouldBeClose")) suite.addTest(HelperTest("testShouldNotBeClose")) return suite def setUp( self ): from classes.helper import Helper self.Helper = Helper() def testShouldReturnInt( self ): a = float(1.5) b = self.Helper.fixed(a) assert type(b) == int, "int was not returned" def testShouldReturnDeltaX( self ): a = 100 b = 2.5 c = self.Helper.delta_x(a,b) self.assertAlmostEqual(c,-1.26591410277) def testShouldReturnDeltaY( self ): a = 100 b = 2.5 c = self.Helper.delta_y(a,b) self.assertAlmostEqual(c,2.15579718072) def testShouldReturnRandomColor( self ): color = self.Helper.random_color() for c in color: self.assertGreaterEqual(c, config.color_min) self.assertLessEqual(c, config.color_max) def testShouldCollide( self ): from classes.target import Target t1 = Target() t2 = Target() t1.x = 100 t1.y = 100 t2.x = 100 t2.y = 100 c = self.Helper.is_collided(t1,t2) assert c == True, "collision not detected" def testShouldNotCollide( self ): from classes.target import Target t1 = Target() t2 = Target() t1.x = 200 t1.y = 200 t2.x = 100 t2.y = 100 c = self.Helper.is_collided(t1,t2) assert c == False, "collision detected" def testShouldBeClose( self ): from classes.target import Target t1 = Target() t2 = Target() t1.x = 150 t1.y = 150 t2.x = 100 t2.y = 100 c = self.Helper.is_close(t1,t2) assert c == True, "objects are not close" def testShouldNotBeClose( self ): from classes.target import Target t1 = Target() t2 = Target() t1.x = 550 t1.y = 550 t2.x = 100 t2.y = 100 c = self.Helper.is_close(t1,t2) assert c == False, "objects close" def tearDown( self ): self.member = None
def setUp( self ): from classes.helper import Helper self.Helper = Helper()