Exemple #1
0
async def add_to_cache(bot, url, name=None, file_location=None):
    """Downloads the URL and saves to the audio cache folder.

    If the cache folder has surpassed the cache size, this will continually
    remove the least used file (by date) until there is enough space. If the
    downloaded file is more than half the size of the total cache, it will not
    be stored. Returns the final location of the downloaded file.

    If name is specified, it will be stored under that name instead of the url.
    If file_location is specified, it will move that file instead of
    downloading the URL.
    """
    if file_location:
        cleaned_name = utilities.get_cleaned_filename(file_location)
    else:
        file_location, cleaned_name = await utilities.download_url(bot, url, include_name=True)
    if name:
        cleaned_name = utilities.get_cleaned_filename(name)
    try:
        download_stat = os.stat(file_location)
    except FileNotFoundError:
        raise CBException("The audio could not be saved. Please try again later.")
    cache_limit = configurations.get(bot, 'core', 'cache_size_limit') * 1000 * 1000
    store = cache_limit > 0 and download_stat.st_size < cache_limit / 2

    # Ensure that the target destination does not exist (remove it if it does)
    if store:
        cached_location = '{0}/audio_cache/{1}'.format(bot.path, cleaned_name)
    else:
        cached_location = '{}/temp/tempsound'.format(bot.path)
    try:
        os.remove(cached_location)
    except:  # Doesn't matter if file doesn't exist
        pass
    os.rename(file_location, cached_location)
    os.utime(cached_location)

    if store:
        cache_entries = []
        total_size = 0
        for entry in os.scandir('{}/audio_cache'.format(bot.path)):
            stat = entry.stat()
            cache_entries.append((stat.st_atime, stat.st_size, entry.path))
            total_size += stat.st_size
        cache_entries.sort(reverse=True)

        while total_size > cache_limit:
            _, size, path = cache_entries.pop()
            logger.info("Removing from cache: %s", path)
            os.remove(path)
            total_size -= size
    else:
        logger.info("Not storing %s. Download size: %s", file_location, download_stat.st_size)

    try:
        download_stat = os.stat(cached_location)
    except FileNotFoundError:
        raise CBException("The file was not moved properly.")
    return cached_location
Exemple #2
0
async def add_to_cache(bot, url, name=None, file_location=None):
    """Downloads the URL and saves to the audio cache folder.

    If the cache folder has surpassed the cache size, this will continually
    remove the least used file (by date) until there is enough space. If the
    downloaded file is more than half the size of the total cache, it will not
    be stored. Returns the final location of the downloaded file.

    If name is specified, it will be stored under that name instead of the url.
    If file_location is specified, it will move that file instead of
    downloading the URL.
    """
    if file_location:
        cleaned_name = utilities.get_cleaned_filename(file_location)
    else:
        file_location, cleaned_name = await utilities.download_url(
            bot, url, include_name=True)
    if name:
        cleaned_name = utilities.get_cleaned_filename(name)
    try:
        download_stat = os.stat(file_location)
    except FileNotFoundError:
        raise BotException(
            EXCEPTION, "The audio could not be saved. Please try again later.")
    cache_limit = bot.configurations['core']['cache_size_limit'] * 1000 * 1000
    store = cache_limit > 0 and download_stat.st_size < cache_limit / 2

    if store:
        cached_location = '{0}/audio_cache/{1}'.format(bot.path, cleaned_name)
    else:
        cached_location = '{}/temp/tempsound'.format(bot.path)
    try:
        os.remove(cached_location)
    except:  # Doesn't matter if file doesn't exist
        pass
    os.rename(file_location, cached_location)

    if store:
        cache_entries = []
        total_size = 0
        for entry in os.scandir('{}/audio_cache'.format(bot.path)):
            stat = entry.stat()
            cache_entries.append((stat.st_atime, stat.st_size, entry.path))
            total_size += stat.st_size
        cache_entries.sort(reverse=True)

        # TODO: Check complexity of list entry removal
        while total_size > cache_limit:
            entry = cache_entries.pop()
            os.remove(entry[2])
            total_size -= entry[1]

    return cached_location
Exemple #3
0
def _search_games(bot, search, guild_id=None, return_game=False):
    """Searches the schedule for the given game and gets the information."""
    cleaned_search = utilities.get_cleaned_filename(search, cleaner=True)
    schedule_data = data.get(bot, __name__, 'schedule', volatile=True)
    found_games = []
    for index, game in enumerate(schedule_data):
        if cleaned_search == game['key']:
            found_games = [game]
            break
        elif cleaned_search in game['key']:
            found_games.append(game)
    if not found_games:
        raise CBException("No games found with that name.")
    elif len(found_games) > 10:
        raise CBException("Too many games found with that name.")
    elif len(found_games) != 1:
        raise CBException(
            "Multiple games found:", '\n'.join([
                '{} ({})'.format(game['game'], game['type'])
                for game in found_games
            ]))
    elif return_game:
        return found_games[0]
    else:
        return _embed_games_information(bot, found_games, guild_id)[0]
Exemple #4
0
async def add_to_cache_ydl(bot, downloader, url):
    """Downloads the given URL using YoutubeDL and stores it in the cache.

    The downloader must be provided as a YoutubeDL downloader object.
    """
    file_location = '{}/temp/tempsound_{}'.format(bot.path, utilities.get_cleaned_filename(url))
    downloader.params.update({'outtmpl': file_location})
    await utilities.future(downloader.download, [url])
    return await add_to_cache(bot, None, name=url, file_location=file_location)
Exemple #5
0
def get_from_cache(bot, name, url=None):
    """Gets the filename from the audio_cache. Returns None otherwise.

    If url is specified, it will clean it up and look for that instead. This
    also sets the found file's access time.
    """
    if url:
        name = utilities.get_cleaned_filename(url)
    file_path = '{0}/audio_cache/{1}'.format(bot.path, name)
    if os.path.isfile(file_path):
        os.utime(file_path, None)
        return file_path
    else:
        return None
Exemple #6
0
def get_from_cache(bot, name, url=None):
    """Gets the filename from the audio_cache. Returns None otherwise.

    If url is specified, it will clean it up and look for that instead. This
    also sets the found file's access time.
    """
    if url:
        name = utilities.get_cleaned_filename(url)
    file_path = '{0}/audio_cache/{1}'.format(bot.path, name)
    if os.path.isfile(file_path):
        os.utime(file_path)
        return file_path
    else:
        return None
Exemple #7
0
def _search_games(bot, search, guild_id=None, return_game=False):
    """Searches the schedule for the given game and gets the information."""
    cleaned_search = utilities.get_cleaned_filename(search, cleaner=True)
    schedule_data = data.get(bot, __name__, 'schedule', volatile=True)
    found_games = []
    for index, game in enumerate(schedule_data):
        if cleaned_search == game['key']:
            found_games = [game]
            break
        elif cleaned_search in game['key']:
            found_games.append(game)
    if not found_games:
        raise CBException("No games found with that name.")
    elif len(found_games) > 10:
        raise CBException("Too many games found with that name.")
    elif len(found_games) != 1:
        raise CBException(
            "Multiple games found:", '\n'.join(
                ['{} ({})'.format(game['game'], game['type'])  for game in found_games]))
    elif return_game:
        return found_games[0]
    else:
        return _embed_games_information(bot, found_games, guild_id)[0]
Exemple #8
0
async def _update_schedule(bot):
    """Reads the GDQ schedule and updates the information in the database."""
    schedule_url = configurations.get(bot, __name__, 'schedule_url')
    html_data = (await utilities.future(requests.get, schedule_url)).text
    soup = BeautifulSoup(html_data, 'html.parser')
    run_table = soup.find('table', {'id': 'runTable'})
    schedule_data = []
    game_list = []

    if run_table is None:
        raise CBException('Run table not found!')

    debug_weeks = data.get(bot, __name__, 'debug_weeks', default=0, volatile=True)
    current_data = {}
    for entry in run_table.find_all('tr'):
        entry_class = entry.get('class', [''])[0]

        if entry_class == 'day-split':
            continue

        subentries = [subentry.text for subentry in entry.find_all('td')]
        if subentries[0].startswith(' '):  # Second column
            subentries = subentries[:2]
        if entry_class == 'second-row':  # Extra data for the last game
            estimation, run_type = subentries
            split_estimate = estimation.split(':')
            estimation_seconds = (int(split_estimate[0])*3600 +
                                  int(split_estimate[1])*60 +
                                  int(split_estimate[2]))
            end_time = (
                current_data['scheduled'] + datetime.timedelta(
                    seconds=(estimation_seconds + current_data['setup_seconds'])))
            key_name = utilities.get_cleaned_filename(
                current_data['game'] + run_type, cleaner=True)
            current_data.update({
                'estimation': estimation.strip(),
                'seconds': estimation_seconds,
                'type': run_type,
                'end': end_time,
                'key': key_name
            })
            game_list.append(key_name)
            schedule_data.append(current_data)

        else:  # Happens first
            while len(subentries) < 4:
                subentries.append('')
            start_time_string, game, runners, setup_time = subentries
            start_time = datetime.datetime.strptime(start_time_string, '%Y-%m-%dT%H:%M:%SZ')
            setup_time = setup_time.strip()
            split_setup = setup_time.split(':')
            if len(split_setup) > 1:
                setup_seconds = (int(split_setup[0])*3600 +
                                 int(split_setup[1])*60 +
                                 int(split_setup[2]))
            else:
                setup_seconds = 0
            current_data = {
                'scheduled': start_time - datetime.timedelta(weeks=debug_weeks),
                'game': game,
                'runners': runners,
                'setup': setup_time,
                'setup_seconds': setup_seconds
            }

    # Add finale entry
    run_type = 'Party%'
    end_time = current_data['scheduled'] + datetime.timedelta(minutes=30)
    key_name = utilities.get_cleaned_filename(current_data['game'] + run_type, cleaner=True)
    current_data.update({
        'estimation': '0:30:00',
        'seconds': 60*30,
        'end': end_time,
        'type': run_type,
        'key': key_name
    })
    game_list.append(key_name)
    schedule_data.append(current_data)

    # Update scheduled notifications
    entries = utilities.get_schedule_entries(bot, __name__)
    for entry in entries:
        payload, key = entry[3:5]
        if key not in game_list:  # Not found error
            error_message = (
                ":warning: Warning: The game {} has been removed, renamed, or "
                "recategorized. You have been removed from the notification list "
                "for this game. Please check the schedule at {}.".format(
                    payload['text'], configurations.get(bot, __name__, 'schedule_url')))
            utilities.update_schedule_entries(
                bot, __name__, search=key, payload={'error': error_message}, new_time=time.time())
        else:
            game = schedule_data[game_list.index(key)]
            start_time, end_time = game['scheduled'], game['end']
            setup_delta = datetime.timedelta(seconds=game['setup_seconds'])
            scheduled = start_time.replace(tzinfo=datetime.timezone.utc).timestamp()
            current_time = datetime.datetime.utcnow()
            if start_time + setup_delta < current_time < end_time:
                stream_url = configurations.get(bot, __name__, 'stream_url')
                payload = {'error': (
                        "Uh oh. The schedule shifted drastically and I didn't notice "
                        "fast enough - sorry! {} is live right now at {}").format(
                            payload['text'], stream_url)}
            else:
                payload.update({'end': scheduled + game['seconds']})
            utilities.update_schedule_entries(
                bot, __name__, search=key, payload=payload, new_time=scheduled)

    # Save data
    data.add(bot, __name__, 'schedule', schedule_data, volatile=True)
    try:
        _update_current_game(bot)
    except:
        pass
Exemple #9
0
async def _update_schedule(bot):
    """Reads the GDQ schedule and updates the information in the database."""
    schedule_url = configurations.get(bot, __name__, 'schedule_url')
    html_data = (await utilities.future(requests.get, schedule_url)).text
    soup = BeautifulSoup(html_data, 'html.parser')
    run_table = soup.find('table', {'id': 'runTable'})
    schedule_data = []
    game_list = []

    if run_table is None:
        raise CBException('Run table not found!')

    debug_weeks = data.get(bot,
                           __name__,
                           'debug_weeks',
                           default=0,
                           volatile=True)
    current_data = {}
    for entry in run_table.find_all('tr'):
        entry_class = entry.get('class', [''])[0]

        if entry_class == 'day-split':
            continue

        subentries = [subentry.text for subentry in entry.find_all('td')]
        if entry_class == 'second-row':  # Extra data for the last game
            estimation, run_type = subentries
            split_estimate = estimation.split(':')
            estimation_seconds = (int(split_estimate[0]) * 3600 +
                                  int(split_estimate[1]) * 60 +
                                  int(split_estimate[2]))
            end_time = (current_data['scheduled'] + datetime.timedelta(
                seconds=(estimation_seconds + current_data['setup_seconds'])))
            key_name = utilities.get_cleaned_filename(current_data['game'] +
                                                      run_type,
                                                      cleaner=True)
            current_data.update({
                'estimation': estimation.strip(),
                'seconds': estimation_seconds,
                'type': run_type,
                'end': end_time,
                'key': key_name
            })
            game_list.append(key_name)
            schedule_data.append(current_data)

        else:  # Happens first
            while len(subentries) < 4:
                subentries.append('')
            start_time_string, game, runners, setup_time = subentries
            start_time = datetime.datetime.strptime(start_time_string,
                                                    '%Y-%m-%dT%H:%M:%SZ')
            setup_time = setup_time.strip()
            split_setup = setup_time.split(':')
            if len(split_setup) > 1:
                setup_seconds = (int(split_setup[0]) * 3600 +
                                 int(split_setup[1]) * 60 +
                                 int(split_setup[2]))
            else:
                setup_seconds = 0
            current_data = {
                'scheduled':
                start_time - datetime.timedelta(weeks=debug_weeks),
                'game': game,
                'runners': runners,
                'setup': setup_time,
                'setup_seconds': setup_seconds
            }

    # Add finale entry
    run_type = 'Party%'
    end_time = current_data['scheduled'] + datetime.timedelta(minutes=30)
    key_name = utilities.get_cleaned_filename(current_data['game'] + run_type,
                                              cleaner=True)
    current_data.update({
        'estimation': '0:30:00',
        'seconds': 60 * 30,
        'end': end_time,
        'type': run_type,
        'key': key_name
    })
    game_list.append(key_name)
    schedule_data.append(current_data)

    # Update scheduled notifications
    entries = utilities.get_schedule_entries(bot, __name__)
    for entry in entries:
        payload, key = entry[3:5]
        if key not in game_list:  # Not found error
            error_message = (
                ":warning: Warning: The game {} has been removed, renamed, or "
                "recategorized. You have been removed from the notification list "
                "for this game. Please check the schedule at {}.".format(
                    payload['text'],
                    configurations.get(bot, __name__, 'schedule_url')))
            utilities.update_schedule_entries(bot,
                                              __name__,
                                              search=key,
                                              payload={'error': error_message},
                                              time=time.time())
        else:
            game = schedule_data[game_list.index(key)]
            start_time, end_time = game['scheduled'], game['end']
            setup_delta = datetime.timedelta(seconds=game['setup_seconds'])
            scheduled = start_time.replace(
                tzinfo=datetime.timezone.utc).timestamp()
            current_time = datetime.datetime.utcnow()
            if start_time + setup_delta < current_time < end_time:
                stream_url = configurations.get(bot, __name__, 'stream_url')
                payload = {
                    'error':
                    ("Uh oh. The schedule shifted drastically and I didn't notice "
                     "fast enough - sorry! {} is live right now at {}").format(
                         payload['text'], stream_url)
                }
            else:
                payload.update({'end': scheduled + game['seconds']})
            utilities.update_schedule_entries(bot,
                                              __name__,
                                              search=key,
                                              payload=payload,
                                              time=scheduled)

    # Save data
    data.add(bot, __name__, 'schedule', schedule_data, volatile=True)
    try:
        _update_current_game(bot)
    except:
        pass