def site_resources(args: str) -> Dict[str, str]: results = {} match = re.match('^s? ?([0-9]*|all) +', args) if match: season_prefix = 'seasons/' + match.group(1) args = args.replace(match.group(0), '', 1).strip() else: season_prefix = '' if ' ' in args: area, detail = args.split(' ', 1) else: area, detail = args, '' if area == 'archetype': area = 'archetypes' if area == 'card': area = 'cards' if area == 'person': area = 'people' sitemap = fetcher.sitemap() matches = [ endpoint for endpoint in sitemap if endpoint.startswith('/{area}/'.format(area=area)) ] if len(matches) > 0: detail = '{detail}/'.format( detail=fetch_tools.escape(detail, True)) if detail else '' url = fetcher.decksite_url('{season_prefix}/{area}/{detail}'.format( season_prefix=season_prefix, area=fetch_tools.escape(area), detail=detail)) results[url] = args return results
def times_from_location(q: str, twentyfour: bool) -> Dict[str, List[str]]: api_key = configuration.get('google_maps_api_key') if not api_key: raise NotConfiguredException('No value found for google_maps_api_key') url = 'https://maps.googleapis.com/maps/api/geocode/json?address={q}&key={api_key}&sensor=false'.format( q=fetch_tools.escape(q), api_key=api_key) info = fetch_tools.fetch_json(url) if 'error_message' in info: return info['error_message'] try: location = info['results'][0]['geometry']['location'] except IndexError as e: raise TooFewItemsException(e) from e url = 'https://maps.googleapis.com/maps/api/timezone/json?location={lat},{lng}×tamp={timestamp}&key={api_key}&sensor=false'.format( lat=fetch_tools.escape(str(location['lat'])), lng=fetch_tools.escape(str(location['lng'])), timestamp=fetch_tools.escape(str(dtutil.dt2ts(dtutil.now()))), api_key=api_key) timezone_info = fetch_tools.fetch_json(url) if 'error_message' in timezone_info: return timezone_info['error_message'] if timezone_info['status'] == 'ZERO_RESULTS': raise TooFewItemsException(timezone_info['status']) try: timezone = dtutil.timezone(timezone_info['timeZoneId']) except KeyError as e: raise TooFewItemsException( f'Unable to find a timezone in {timezone_info}') from e return { current_time(timezone, twentyfour): [info['results'][0]['formatted_address']] }
def scryfall_image(c: Card, version: str = '', face: str = None) -> str: if face == 'meld': name = c.names[1] elif ' // ' in c.name: name = c.name.replace(' // ', '/') else: name = c.name u = 'https://api.scryfall.com/cards/named?exact={c}&format=image'.format( c=escape(name)) if version: u += '&version={v}'.format(v=escape(version)) if face and face != 'meld': u += '&face={f}'.format(f=escape(face)) return u
def search_scryfall(query: str) -> Tuple[int, List[str], List[str]]: """Returns a tuple. First member is an integer indicating how many cards match the query total, second member is a list of card names up to the maximum that could be fetched in a timely fashion.""" if query == '': return 0, [], [] print(f'Searching scryfall for `{query}`') result_json = fetch_tools.fetch_json( 'https://api.scryfall.com/cards/search?q=' + fetch_tools.escape(query), character_encoding='utf-8') if 'code' in result_json.keys(): # The API returned an error if result_json['status'] == 404: # No cards found return 0, [], [] print('Error fetching scryfall data:\n', result_json) return 0, [], [] for warning in result_json.get( 'warnings', []): # scryfall-provided human-readable warnings print(warning) result_data = result_json['data'] result_data.sort(key=lambda x: x['legalities']['penny']) def get_frontside(scr_card: Dict) -> str: """If card is transform, returns first name. Otherwise, returns name. This is to make sure cards are later found in the database""" # not sure how to handle meld cards if scr_card['layout'] in ['transform', 'flip', 'modal_dfc']: return scr_card['card_faces'][0]['name'] return scr_card['name'] result_cardnames = [get_frontside(obj) for obj in result_data] return result_json['total_cards'], result_cardnames, result_json.get( 'warnings', [])
def scryfall_image(c: Card, version: str = '', face: str = None) -> str: if face == 'meld': name = c.names[1] elif ' // ' in c.name: name = c.name.replace(' // ', '/') else: name = c.name p = oracle.get_printing(c, c.get('preferred_printing')) if p is not None: u = f'https://api.scryfall.com/cards/{p.set_code}/{p.number}?format=image' else: u = 'https://api.scryfall.com/cards/named?exact={c}&format=image'.format(c=escape(name)) if version: u += '&version={v}'.format(v=escape(version)) if face and face != 'meld': u += '&face={f}'.format(f=escape(face)) return u
def card_rulings(c: Card) -> str: raw_rulings = fetcher.rulings(c.name) comments = [r['comment'] for r in raw_rulings] if len(comments) > 3: n = len(comments) - 2 comments = comments[:2] comments.append( 'And {n} others. See <https://scryfall.com/search?q=%21%22{cardname}%22#rulings>' .format(n=n, cardname=fetch_tools.escape(c.name))) return '\n'.join(comments) or 'No rulings available.'
def search_scryfall(query: str, exhaustive: bool = False) -> Tuple[int, List[str]]: """Returns a tuple. First member is an integer indicating how many cards match the query total, second member is a list of card names up to the maximum that could be fetched in a timely fashion. Supply exhaustive=True to instead retrieve the full list (potentially very slow).""" if query == '': return False, [] redis_key = f'scryfall:query:{query}:' + ('exhaustive' if exhaustive else 'nonexhaustive') cached = redis.get_list(redis_key) result_data: List[Dict] if cached: total_cards, result_data = int(cached[0]), cached[1] else: url = 'https://api.scryfall.com/cards/search?q=' + fetch_tools.escape( query) result_data = [] while True: for _ in range(3): try: result_json = fetch_tools.fetch_json(url) break except FetchException as c: print(c) if 'code' in result_json.keys(): # The API returned an error if result_json['status'] == 404: # No cards found return False, [] print('Error fetching scryfall data:\n', result_json) return False, [] for warning in result_json.get( 'warnings', []): # scryfall-provided human-readable warnings print(warning) # Why aren't we displaying these to the user? result_data += result_json['data'] total_cards = int(result_json['total_cards']) if not exhaustive or len(result_data) >= total_cards: break sleep(0.1) url = result_json['next_page'] redis.store(redis_key, [total_cards, result_data], ex=3600) result_data.sort(key=lambda x: x['legalities']['penny']) def get_frontside(scr_card: Dict) -> str: """If card is transform, returns first name. Otherwise, returns name. This is to make sure cards are later found in the database""" # not sure how to handle meld cards if scr_card['layout'] in [ 'transform', 'flip', 'adventure', 'modal_dfc' ]: return scr_card['card_faces'][0]['name'] return scr_card['name'] result_cardnames = [get_frontside(obj) for obj in result_data] return total_cards, result_cardnames
def cardhoarder_url( self ) -> str: # This should be a Deck, but we can't import it from here. d = self.deck cs: Dict[str, int] = {} for entry in d.maindeck + d.sideboard: name = entry.name cs[name] = cs.get(name, 0) + entry['n'] deck_s = '||'.join([ str(v) + ' ' + card.to_mtgo_format(k).replace('"', '') for k, v in cs.items() ]) return 'https://www.cardhoarder.com/decks/upload?deck={deck}'.format( deck=fetch_tools.escape(deck_s))
def card_history(c: Card) -> str: data: Dict[int, bool] = {} for format_name, status in c.legalities.items(): if 'Penny Dreadful ' in format_name and status == 'Legal': season_id = seasons.SEASONS.index( format_name.replace('Penny Dreadful ', '')) + 1 data[season_id] = True data[seasons.current_season_num()] = c.legalities.get('Penny Dreadful', None) == 'Legal' s = ' ' for i in range(1, seasons.current_season_num() + 1): s += f'{i} ' s += ':white_check_mark:' if data.get(i, False) else ':no_entry_sign:' s += ' ' s = s.strip() s += '\n<' + fetcher.decksite_url('/seasons/all/cards/{name}/'.format( name=fetch_tools.escape(c.name, skip_double_slash=True))) + '>' return s
def bluebones_image(cards: List[Card]) -> str: c = '|'.join(c.name for c in cards) return 'http://magic.bluebones.net/proxies/index2.php?c={c}'.format( c=escape(c))
async def buglink(ctx: MtgContext, *, c: Card) -> None: """Link to the modo-bugs page for a card.""" base_url = 'https://github.com/PennyDreadfulMTG/modo-bugs/issues' if c is None: await ctx.send(base_url) return msg = '<{base_url}?utf8=%E2%9C%93&q=is%3Aissue+%22{name}%22>'.format(base_url=base_url, name=fetch_tools.escape(c.name)) if not c.bugs or len(c.bugs) == 0: msg = "I don't know of a bug for {name} but here's the link: {link}".format(name=c.name, link=msg) await ctx.send(msg)