Exemple #1
0
def merge_card_images(cards: List[Dict[str, Any]]) -> str:
    """
    Taken from SO, but this method will merge all images in the
    images folder into one image. All prior images will be
    side-by-side
    :param cards: Cards to merge into one image
    :return: Resting URL of merged image
    """
    cards_to_merge: List[str] = glob.glob(os.path.join(TEMP_CARD_DIR, '*.png'))

    images: Iterator[Any] = map(PIL.Image.open, cards_to_merge)
    widths, heights = zip(*(i.size for i in images))

    total_width = sum(widths)
    max_height = max(heights)

    new_im = PIL.Image.new('RGB', (total_width, max_height))

    images = map(PIL.Image.open, cards_to_merge)
    x_offset = 0
    for im in images:
        new_im.paste(im, (x_offset, 0))
        x_offset += im.size[0]

    combined_name: str = '{}-{}.png'.format(cards[0]['name'].replace('/', '_'),
                                            cards[1]['name'].replace('/', '_'))
    save_url: str = os.path.join(TEMP_CARD_DIR, combined_name)

    new_im.save(save_url)
    logging.info('Saved merged image to {}'.format(save_url))

    return save_url
Exemple #2
0
def delete_temp_cards() -> None:
    """
    Delete the PNG images in the image folder
    """
    for card in glob.glob(os.path.join(TEMP_CARD_DIR, '*.png')):
        logging.info('Deleting file {}'.format(card))
        os.remove(card)
def download_and_save_card_images(cards: List[Dict[str, Any]]) -> None:
    """
    Download and (temporarily) save card images for tweet processing
    :param cards: Cards to download and store
    """
    for card in cards:
        card_image_url: str = card['image_uris']['png']
        request_image = download_contents(card_image_url, 'image')
        with open(os.path.join(TEMP_CARD_DIR, '{}.png'.format(card['name'].replace('//', '_'))), 'wb') as out_file:
            shutil.copyfileobj(request_image.raw, out_file)
        logging.info('Saving image of card {}'.format(card['name']))
Exemple #4
0
def start_game(force_new: bool = False) -> None:
    """
    Start the process of validating an old game and/or creating a new one
    :param force_new: Start a new game, even if one is running. End the old game
    """
    # If contest is over, print results and continue. Otherwise exit
    if is_active_contest_already(force_new):
        exit(0)

    # Clear out the cards directory
    delete_temp_cards()

    # Get 2 random cards
    cards: List[Dict[str, Any]] = download_random_cards(2)
    card1 = '{}: {}'.format(
        cards[0]['name'], cards[0]['scryfall_uri'].replace('api', 'card_golf'))
    card2 = '{}: {}'.format(
        cards[1]['name'], cards[1]['scryfall_uri'].replace('api', 'card_golf'))

    for card in cards:
        logging.info('Card to merge: {}'.format(card['name']))

    # Save the images
    download_and_save_card_images(cards)

    # Merge the images
    tweet_image_url: str = merge_card_images(cards)

    message = (
        "Can you make both of these cards show up in a Scryfall search without using 'or'?\n"
        "• {}\n"
        "• {}\n"
        "Reply to this tweet with a Scryfall URL in the next 24 hours to enter!"
    ).format(card1, card2)

    # Send the tweet
    tweet_id: int = send_tweet(message, tweet_image_url)

    json_entry: Dict[str, Any] = {
        'tweet_id':
        tweet_id,
        'cards': [{
            'name': cards[0]['name'],
            'url': cards[0]['scryfall_uri'],
        }, {
            'name': cards[1]['name'],
            'url': cards[1]['scryfall_uri'],
        }],
    }

    write_to_json_db(TWEET_DATABASE, json_entry)
Exemple #5
0
def download_contents(url: str, download_type: str = 'json') -> Any:
    """
    Download contents from a URL
    :param url: URL to download
    :param download_type: Type of download (Default to JSON)
    :return: Contents
    """
    request_response: Any = {}
    if download_type == 'json':
        request_response = requests.get(url=url).json()
    elif download_type == 'image':
        request_response = requests.get(url=url, stream=True)

    logging.info('Downloaded URL {}'.format(url))
    return request_response
Exemple #6
0
def test_query(user_name: str, scryfall_url: str) -> str:
    """
    Load up the Scryfall URL tweeted by the user and see if it
    matches the competition requirements (i.e. is it exclusively
    the two cards we are looking for)
    :param user_name: Twitter username
    :param scryfall_url: Scryfall URL they tweeted
    :return: Winning query ('' if failed)
    """
    try:
        query: str = urlparse.parse_qs(
            urlparse.urlparse(scryfall_url).query)['q'][0]

        scryfall_api_url = 'https://api.scryfall.com/cards/search?q={}'.format(
            urlparse.quote_plus(query))
        response: Dict[str, Any] = download_contents(scryfall_api_url)

        if response['total_cards'] != 2:
            logging.info('{} result has wrong number of cards: {}'.format(
                user_name, response['total_cards']))

        json_db: Dict[str, Any] = load_json_db(TWEET_DATABASE)
        max_key: str = max(json_db.keys())
        valid_cards: List[str] = [
            json_db[max_key]['cards'][0]['name'],
            json_db[max_key]['cards'][1]['name']
        ]
        for card in response['data']:
            if card['name'] not in valid_cards:
                logging.info('{} result has wrong card: {}'.format(
                    user_name, card['name']))
                return ''

        if 'or' in query.lower():
            logging.info("{} was correct, but they used 'OR': {}".format(
                user_name, query))
            return ''

        # Correct response!
        logging.info('{} was correct! [ {} ] ({})'.format(
            user_name, query, len(query)))
        return urlparse.unquote(query)
    except KeyError:
        logging.info('{} submitted a bad Scryfall URL: {}'.format(
            user_name, scryfall_url))
        return ''
Exemple #7
0
def send_tweet(message_to_tweet: str, url_to_media: str) -> int:
    """
    Send a tweet with an image.
    :param message_to_tweet: Message to send
    :param url_to_media: Image to upload
    :return: Tweet ID (-1 if it failed)
    :raises Exception: Tweet failed to send for some reason
    """
    logging.info('Tweet to send: {}'.format(message_to_tweet))
    try:
        if url_to_media is not None:
            resize_image(url_to_media)
            photo = open(url_to_media, 'rb')
            status = twitter_api.request('statuses/update_with_media',
                                         {'status': message_to_tweet},
                                         {'media[]': photo})
            logging.info('Twitter Status Code: {}'.format(status.status_code))

            response = TwitterAPI.TwitterResponse(status, False).json()
            logging.info('Twitter Response Parsed: {}'.format(response))
            return int(response['id_str'])
        raise Exception("No image attached to tweet")
    except UnicodeDecodeError:
        logging.exception(
            'Your message could not be encoded. Perhaps it contains non-ASCII characters?'
        )
        raise Exception("Tweet failed to send")
Exemple #8
0
def get_results() -> List[Dict[str, Any]]:
    """
    Get the results from the competition and print it out
    :return: Winner's name and their query
    """
    valid_entries: List[Dict[str, Any]] = []

    logging.info('GET RESULTS')

    json_db: Dict[str, Any] = load_json_db(TWEET_DATABASE)
    max_key: str = max(json_db.keys())

    r = TwitterAPI.TwitterPager(twitter_api, 'statuses/mentions_timeline', {
        'count': 200,
        'since_id': json_db[max_key]['tweet_id']
    })

    for item in r.get_iterator():
        if 'text' not in item:
            logging.warning('SUSPEND, RATE LIMIT EXCEEDED: %s\n' %
                            item['message'])
            break

        logging.info('[TWEET] ' + item['user']['screen_name'] + ': ' +
                     item['text'])
        for url in item['entities']['urls']:
            test_url = url['expanded_url']
            if 'scryfall.com' not in test_url:
                continue

            logging.info('{} submitted solution: {}'.format(
                item['user']['screen_name'], test_url))
            test_query_results = test_query(item['user']['screen_name'],
                                            test_url)
            if test_query_results:
                valid_entries.append({
                    'name': item['user']['screen_name'],
                    'length': len(test_query_results),
                    'query': test_query_results
                })

    return valid_entries