def autologin(requester: nsapi.NSRequester, nations: Mapping[str, str], isAutologin: bool) -> Mapping[str, Optional[Result]]: """Autologins a list of nations, from the nations dict of {nation: password/autologin}. If isAutologin is true, the passwords are interpreted as autologins. Returns a dict mapping from nations to autologins. If the autologin value of the returned mapping is None, that means the login failed. """ output: Dict[str, Optional[Result]] = {} for nation, password in nations.items(): # Check how to interpret password if isAutologin: nationAPI = requester.nation(nation, nsapi.Auth(autologin=password)) else: nationAPI = requester.nation(nation, nsapi.Auth(password=password)) # Try making the shard request try: shards = nationAPI.shards("region", "ping", "wa") except nsapi.APIError: output[nation] = None else: output[nation] = Result( autologin=nationAPI.get_autologin(), region=shards["region"], wa=shards["unstatus"].startswith("WA"), ) return output
def unendorsed_nations_v2( requester: nsapi.NSRequester, endorser: str ) -> Tuple[str, Iterable[str]]: """Finds all WA members of the nation's region who have not been endorsed Returns a tuple containing the region, and a iterable of unendorsed nations """ # Retrieve endorser region and endorsement list logging.info("Collecting WA Members") info = requester.nation(endorser).shards("region", "endorsements") region = info["region"] endorsements = set(info["endorsements"].split(",")) # Retrieve regional citizens by cross referencing residents and wa members citizens = set(requester.wa().shard("members").split(",")) & set( requester.region(region).shard("nations").split(":") ) # Optionally only check nations who havent endorsed the endorser nations = citizens - endorsements # Check each nation's endorsments logging.info("Checking WA members for endorsement") nonendorsed = [ nation for nation in nations if endorser not in requester.nation(nation).shard("endorsements") ] return (region, nonendorsed)
def answer_all( requester: nsapi.NSRequester, name: str, autologin: str, optionNum: Optional[int] = None, ) -> Iterable[Tuple[int, int, str]]: """Answers all available issues on the nation. If optionNum is provided, attempts to answer all issues with that option (starting at 0, so 0 is the first option). If an issue does not have that many options, answers with the last. Counts by available options, not all options. Returns tuples encoding (id, option, description). """ # Create and add authentication to the nation API object nation = requester.nation(name) nation.login(autologin) # Prepare output list output: List[Tuple[int, int, str]] = [] # Iterate through all available issues for issue in nation.issues(): # Use the option requested if optionNum: option = list(issue.options.keys())[optionNum] # If none, answer randomly else: option = random.choice(list(issue.options.keys())) info = nation.answer_issue(issue=issue.id, option=option) # info[1] should be the DESC node output.append((issue.id, option, info[1].text if info[1].text else "")) return output
def check_region(requester: nsapi.NSRequester, region: str, previous: Container[str] = None) -> Sequence[str]: """Checks all nations in the specified region for trading card activity. Makes at most 1 + region population requests (1 for each nation). A nation does not qualify if it is in the `previous` container. """ # If a previous list is not provided, use an empty set if not previous: previous = set() participants = [] # Grabs all residents of the region residents = requester.region(region).shard("nations").split(":") # Make a request for each resident for nation in residents: if nation not in previous: info = requester.nation(nation).deck_info() # Save the nation if it meets any of the requirments if (info.numCards > 0 or info.bank > 0 or info.lastValued or info.lastPackOpened): participants.append(nation) return participants
def unendorsed_nations( requester: nsapi.NSRequester, endorser: str ) -> Tuple[str, Iterable[str]]: """Finds all WA members of the nation's region who have not been endorsed Returns a tuple containing the region, and a iterable of unendorsed nations """ # Collect region region = requester.nation(endorser).shard("region") # Load downloaded nation file # Pack into conversion generator to simplify transformations nationDump = requester.dumpManager().nations() # Pull all nations in the region that are WA members # Use generator because we dont need to generate a list that is never used logging.info("Collecting %s WA Members", region) waMembers = [ nation for nation in nationDump if nation.region == region and nation.WAStatus.startswith("WA") ] # Pull nations who are not endorsed logging.info("Collecting WA members who have not been endorsed") nonendorsed = [ # Save name string, converting to lowercase, underscore format clean_format(nation.name) for nation in waMembers # Check if unendorsed by checking endorsements if endorser not in nation.endorsements ] return (region, nonendorsed)
def login( requester: nsapi.NSRequester, nation: str, autologin: t.Optional[str], password: t.Optional[str], ) -> t.Optional[Result]: """Attempts to log in a nation via NS API. Returns None on failure. At least one of password or autologin must be provided; autologin is used if both are provided. """ # Create API object nationAPI = requester.nation( nation, auth=nsapi.Auth(autologin=autologin, password=password) ) # Try making the shard request try: shards = nationAPI.shards("region", "ping", "wa") except nsapi.APIError: # None indicates any failure return None else: return Result( autologin=nationAPI.get_autologin(), region=shards["region"], wa=shards["unstatus"].startswith("WA"), )
def deployed( requester: nsapi.NSRequester, lead: str, roster: t.Mapping[str, t.Iterable[str]], ) -> t.Collection[str]: """Determine who are deployed and endorsing the lead.""" # Obtain endorsement list of lead endos = map(nsapi.clean_format, requester.nation(lead).endorsements()) # Invert roster into puppet -> main form owners = { nsapi.clean_format(switcher): main for main, switchers in roster.items() for switcher in switchers } # Also include main nations owners.update({main: main for main in roster.keys()}) # Return owners who have a nation endoing lead return [owners[nation] for nation in endos if nation in owners]
def sorted_cards( requester: nsapi.NSRequester, nations: Iterable[str] ) -> Mapping[str, Mapping[str, Mapping[nsapi.CardIdentifier, int]]]: """Searchs the trading card decks of the given nations (by name), and sorts by rarity Returns a mapping with {rarity: {nation: {card: quantity}}} structure. Always includes exactly `common`, `uncommon`, `rare`, `ultra-rare`, `epic`, and `legendary` """ # Prepare output structure with rarities # empty string key should catch any Card objects that dont have a category for some reason out: MutableMapping[str, MutableMapping[str, MutableMapping[nsapi.CardIdentifier, int]]] = { "common": {}, "uncommon": {}, "rare": {}, "ultra-rare": {}, "epic": {}, "legendary": {}, "": {}, } # Sort the deck of each nation for nation in nations: # Retrieve the deck of the nation (1 request) deck = requester.nation(nation).deck() # Parse each card in the deck, sorting into appropriate rarity bin for card in deck: # We dont wrap the out[] indexing in a try, since there should never be a error: # if for some reason the card doesnt have a category, it should have "" and # get placed in that bin rarityDict = out[card.rarity] # ensure this nation exists in this rarity bin try: nationDict = rarityDict[nation] except KeyError: rarityDict[nation] = {} nationDict = rarityDict[nation] # creates the card: num key-value pair if it doesnt exist # otherwise, increment the count try: nationDict[card] += 1 except KeyError: nationDict[card] = 1 return out
def uncrossed(requester: nsapi.NSRequester, nation: str, lead: str, duration: int = 3600) -> Iterable[str]: """Returns a iterable of non-recently endorsed nations sourced from lead endorsements `duration` is the number of seconds to check back in the nation's endo happenings """ # Source endorsement list from lead logging.info("Retrieving lead endorser list") nations: Set[str] = set( requester.nation(nation).shard("endorsements").split(",")) # Retrieve recent endo happenings in text form timestamp = int(time.time()) - duration logging.info("Retrieving endo happenings of nation since %s", timestamp) endos: List[str] = [ happening.text for happening in requester.world().happenings( view=f"nation.{nation}", filter="endo", sincetime=str(timestamp), ) ] # Parse endo happening texts # A text can be one of the following two 'distinct' forms: # based on 'hn67' == nation # '@@hn67@@ endorsed @@hn67_ii@@.' (outgoing) # '@@hn67_ii@@ endorsed @@hn67@@.' (incoming) # we only care about outgoing endorsements # the endorsed nation is retrieved using the following process: # .split() transforms the above outgoing example to ['', 'hn67', 'endorsed', 'hn67_ii', ''] # index [3] retrieves the endorsed nation, index [1] retrieves the endorser # [1] is used to verify outgoing ( by checking == nation), and [3] retrieves the endorsee # Note: Splitting twice, could be optimized with pre-comprehension, for loop, or walrus operator logging.info("Filtering for outgoing endorsements") outgoing: Set[str] = set( text.split("@@")[3] for text in endos if text.split("@@")[1] == nation) # Unendorsed is obtained by subtracting the outgoing endorsements from the lead endorsement list logging.info("Obtaining uncross endorsed nations") unendorsed = nations - outgoing return unendorsed
def retrieve_status(requester: nsapi.NSRequester, nation: str) -> Status: """Retrieves info on the nation provided.""" # Check region, wa, and cte try: shard_info = requester.nation(nation).shards("region", "wa") except nsapi.ResourceError: return Status( nsapi.clean_format(nation), True, None, None, ) else: return Status( nsapi.clean_format(nation), False, shard_info["region"], shard_info["unstatus"], )
def is_wa(requester: nsapi.NSRequester, nation: str) -> bool: """Checks if the nation is in the WA""" try: return requester.nation(nation).wa().startswith("WA") except nsapi.ResourceError: return False