Beispiel #1
0
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}&timestamp={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']]
    }
Beispiel #2
0
def fetch_script_tag(entry: str) -> str:
    parts = entry.split(':')
    library = parts[0]
    file = parts[0] if len(parts) == 1 else parts[1]
    info = fetch_tools.fetch_json(f'https://api.cdnjs.com/libraries/{library}')
    version = info.get('version')
    if not version and library.lower() != library:
        library = library.lower()
        info = fetch_tools.fetch_json(
            f'https://api.cdnjs.com/libraries/{library}')
        version = info.get('version')
    if not version:
        raise DoesNotExistException(f'Could not get version for {library}')
    path = None
    for a in info['assets']:
        if a.get('version') == version:
            for f in a['files']:
                if minified_path(f, file):
                    path = f
                    break
                if unminified_path(f, file):
                    path = f
    if not path:
        raise DoesNotExistException(f'Could not find file for {library}')
    return f'<script defer src="//cdnjs.cloudflare.com/ajax/libs/{library}/{version}/{path}"></script>'
def scrape(name: Optional[str] = None) -> None:
    if name:
        data = fetch_tools.fetch_json(
            gatherling_url(f'/api.php?action=eventinfo&event={name}'))
        process_tournament(data['name'], Event(**data))
    else:
        data = fetch_tools.fetch_json(
            gatherling_url('/api.php?action=recent_events'))
        response = make_api_response(data)
        process(response)
def all_sets() -> List[Dict[str, Any]]:
    try:
        d = json.load(open('sets.json'))
    except FileNotFoundError:
        d = fetch_tools.fetch_json('https://api.scryfall.com/sets')
    assert not d['has_more']
    return d['data']
Beispiel #5
0
def make_final_list() -> None:
    planes = fetch_tools.fetch_json('https://api.scryfall.com/cards/search?q=t:plane%20or%20t:phenomenon')['data']
    bad_names = [p['name'] for p in planes]
    bad_names.extend(BANNED_CARDS)
    files = rotation.files()
    lines: List[str] = []
    for line in fileinput.input(files):
        line = text.sanitize(line)
        if line.strip() in bad_names:
            continue
        lines.append(line)
    scores = Counter(lines).most_common()

    passed: List[str] = []
    for name, count in scores:
        if count >= rotation.TOTAL_RUNS / 2:
            passed.append(name)
    final = list(passed)
    final.sort()
    h = open(os.path.join(configuration.get_str('legality_dir'), 'legal_cards.txt'), mode='w', encoding='utf-8')
    h.write(''.join(final))
    h.close()
    print('Generated legal_cards.txt.  {0}/{1} cards.'.format(len(passed), len(scores)))
    setcode = rotation.next_rotation_ex().mtgo_code
    h = open(os.path.join(configuration.get_str('legality_dir'), f'{setcode}_legal_cards.txt'), mode='w', encoding='utf-8')
    h.write(''.join(final))
    h.close()

    do_push()
Beispiel #6
0
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']:
            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', [])
Beispiel #7
0
def catalog_cardnames() -> List[str]:
    result_json = fetch_tools.fetch_json('https://api.scryfall.com/catalog/card-names')
    names: List[str] = result_json['data']
    for n in names:
        if ' // ' in n:
            names.extend(n.split(' // '))
    return names
def bugged_cards() -> Optional[List[Dict[str, Any]]]:
    try:
        bugs = fetch_tools.fetch_json('https://pennydreadfulmtg.github.io/modo-bugs/bugs.json')
    except FetchException:
        print("WARNING: Couldn't fetch bugs")
        bugs = None
    if bugs is None:
        return None
    return bugs
Beispiel #9
0
def tournament(comp: Dict[str, Any]) -> None:
    comp = fetch_tools.fetch_json(comp['url'])
    dt = dtutil.ts2dt(comp['start_date'])
    de = dtutil.ts2dt(comp['end_date'])
    competition_id = competition.get_or_insert_competition(
        dt, de, comp['name'], comp['series_name'], comp['url'],
        top.Top(comp['top_n']))
    loaded_competition = competition.load_competition(competition_id)
    if loaded_competition.num_decks < comp['num_decks']:
        for d in comp['decks']:
            store_deck(d)
Beispiel #10
0
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
Beispiel #11
0
def scryfall_import(name: str) -> bool:
    sfcard = fetch_tools.fetch_json(
        'https://api.scryfall.com/cards/named?fuzzy={name}'.format(name=name))
    if sfcard['object'] == 'error':
        raise Exception()
    try:
        valid_name(sfcard['name'])
        print(
            f"Not adding {sfcard['name']} to the database as we already have it."
        )
        return False
    except InvalidDataException:
        print(f"Adding {sfcard['name']} to the database as we don't have it.")
        add_cards_and_update([sfcard])
        return True
def whatsinstandard() -> Dict[str, Union[bool, List[Dict[str, str]]]]:
    cached = redis.get_container('magic:fetcher:whatisinstandard_6')
    if cached is not None:
        return cached

    try:
        info = fetch_tools.fetch_json('http://whatsinstandard.com/api/v6/standard.json')
    except FetchException:
        cached = redis.get_container('magic:fetcher:whatisinstandard_noex')
        if cached is not None:
            return cached
        raise
    redis.store('magic:fetcher:whatisinstandard_6', info, ex=86400)
    redis.store('magic:fetcher:whatisinstandard_noex', info)
    return info
async def randomdeck(ctx: MtgContext) -> None:
    """A random deck from the current season."""
    blob = fetch_tools.fetch_json(fetcher.decksite_url('/api/randomlegaldeck'))
    if 'error' in blob or 'url' not in blob:
        await ctx.send(blob.get('msg', ''))
    else:
        ctn = blob.get('competition_type_name', None)
        if ctn is not None:
            if ctn == 'Gatherling' and blob['finish'] == 1:
                record = 'won'
            elif ctn == 'Gatherling' and blob['finish'] <= blob['competition_top_n']:
                record = f"made Top {blob['competition_top_n']} of"
            else:
                draws = f"-{blob['draws']}" if blob['draws'] > 0 else ''
                record = f"went {blob['wins']}-{blob['losses']}{draws} in"
            preamble = f"{blob['person']} {record} {blob['competition_name']} with this:\n"
        else:
            preamble = f"{blob['person']} posted this on {blob['source_name']}:\n"
        await ctx.send(preamble + blob['url'])
async def spoiler(ctx: MtgContext, *, args: str) -> None:
    """Request a card from an upcoming set."""
    if len(args) == 0:
        await ctx.send('{author}: Please specify a card name.'.format(author=ctx.author.mention))
        return
    sfcard = fetch_tools.fetch_json('https://api.scryfall.com/cards/named?fuzzy={name}'.format(name=args))
    if sfcard['object'] == 'error':
        await ctx.send('{author}: {details}'.format(author=ctx.author.mention, details=sfcard['details']))
        return
    imagename = '{set}_{number}'.format(
        set=sfcard['set'], number=sfcard['collector_number'])
    imagepath = '{image_dir}/{imagename}.jpg'.format(image_dir=configuration.get('image_dir'), imagename=imagename)
    if sfcard.get('card_faces') and sfcard.get('layout', '') != 'split':
        c = sfcard['card_faces'][0]
    else:
        c = sfcard
    fetch_tools.store(c['image_uris']['normal'], imagepath)
    text = emoji.replace_emoji('{name} {mana}'.format(name=sfcard['name'], mana=c['mana_cost']), ctx.bot)
    await ctx.send(file=File(imagepath), content=text)
    await oracle.scryfall_import_async(sfcard['name'])
Beispiel #15
0
def disabled() -> None:
    competitions = fetch_tools.fetch_json(
        'https://pennydreadfulmagic.com/api/competitions')
    competitions.reverse()
    for c in competitions:
        tournament(c)
def scryfall_last_updated() -> datetime.datetime:
    d = fetch_tools.fetch_json('https://api.scryfall.com/bulk-data')
    for o in d['data']:
        if o['type'] == 'default_cards':
            return dtutil.parse_rfc3339(o['updated_at'])
    raise InvalidDataException(f'Could not get the last updated date from Scryfall: {d}')
def all_cards() -> List[CardDescription]:
    try:
        f = open('all-default-cards.json')
        return json.load(f)
    except FileNotFoundError:
        return fetch_tools.fetch_json('https://archive.scryfall.com/json/scryfall-default-cards.json', character_encoding='utf-8')
def sitemap() -> List[str]:
    return fetch_tools.fetch_json(decksite_url('/api/sitemap/'))['urls']
def rulings(cardname: str) -> List[Dict[str, str]]:
    card = fetch_tools.fetch_json('https://api.scryfall.com/cards/named?exact={name}'.format(name=cardname))
    return fetch_tools.fetch_json(card['uri'] + '/rulings')['data']
def fetch_decks() -> List[RawDeckType]:
    return fetch_tools.fetch_json('https://tappedout.net/api/deck/latest/penny-dreadful/')
def fetch_deck_details(raw_deck: RawDeckType) -> RawDeckType:
    return fetch_tools.fetch_json('https://tappedout.net/api/collection/collection:deck/{slug}/'.format(slug=raw_deck['slug']))
def card_price(cardname: str) -> Dict[str, Any]:
    return fetch_tools.fetch_json('http://vorpald20.com:5800/{0}/'.format(cardname.replace('//', '-split-')))