Esempio n. 1
0
def convert_core(bot, guild):
    if data.get(bot, 'core', None, guild_id=guild.id):
        logger.warn("Guild %s (%s) already had core converted", guild.name,
                    guild.id)
        return
    base_data = data.get(bot, 'base', None, guild_id=guild.id, default={})
    if 'disabled' in base_data:
        # TODO: Iterate through toggled commands
        pass
    if 'blocked' in base_data:
        replacement = []
        for entry in base_data['blocked']:
            replacement.append(int(entry))
        base_data['blocked'] = replacement
    if 'muted_channels' in base_data:
        replacement = []
        for entry in base_data['muted_channels']:
            replacement.append(int(entry))
        base_data['muted_channels'] = replacement
    if 'moderators' in base_data:
        del base_data['moderators']
    if base_data:
        for key, value in base_data.items():
            data.add(bot, 'core', key, value, guild_id=guild.id)
        data.remove(bot, 'base', None, guild_id=guild.id)
Esempio n. 2
0
async def commission_configure(bot, context):
    """Configures the channel and cooldown for the commission channel rules."""
    rules = data.get(bot,
                     __name__,
                     'rules',
                     guild_id=context.guild.id,
                     default={})
    default_cooldown = configurations.get(bot, __name__, 'default_cooldown')
    replace_channel = context.options.get('channel', rules.get('channel'))
    replace_cooldown = context.options.get(
        'cooldown', rules.get('cooldown', default_cooldown))
    if not replace_channel:
        raise CBException("No commission channel configured.")

    # Reset advertisement data
    rules = {'channel': replace_channel, 'cooldown': replace_cooldown}
    data.add(bot, __name__, 'rules', rules, guild_id=context.guild.id)
    data.remove(bot,
                __name__,
                'advertisements',
                guild_id=context.guild.id,
                volatile=True,
                safe=True)
    await _get_advertisement_data(bot, context.guild)

    description = 'Channel: {0.mention}\nCooldown: {1}'.format(
        data.get_channel(bot, replace_channel),
        utilities.get_time_string(replace_cooldown, text=True, full=True))
    embed = discord.Embed(title='Commission channel configuration:',
                          description=description)
    return Response(embed=embed)
Esempio n. 3
0
async def _get_buffered_donation_stats(bot):
    """Pulls buffered donation information if it is 1 minute old or less."""
    last_pull = data.get(bot, __name__, 'last_pull', volatile=True, default=0)
    buffer_time = configurations.get(bot, __name__, 'stats_buffer_time')
    if time.time() - last_pull > buffer_time:  # Pull information
        data.add(bot, __name__, 'last_pull', time.time(), volatile=True)
        tracker_url = configurations.get(bot, __name__, 'tracker_url')
        try:
            donate_html = (await utilities.future(requests.get,
                                                  tracker_url)).text
            soup = BeautifulSoup(donate_html, 'html.parser')
            donation_text = soup.find('small').text.splitlines()[1:]
            total_raised, total_donations, _unused = donation_text[1].split()
            total_donations = total_donations.strip('()')
            max_average = donation_text[3]
        except Exception as e:
            raise CBException("Failed to retrieve donation data.", e=e)
        donation_stats = [total_raised, total_donations, max_average]
        data.add(bot,
                 __name__,
                 'donation_stats',
                 donation_stats,
                 volatile=True)
    else:
        donation_stats = data.get(bot,
                                  __name__,
                                  'donation_stats',
                                  volatile=True)
    return donation_stats
Esempio n. 4
0
def set_player(bot, server_id, player):
    """Sets the voice player of the given server."""
    data.add(bot,
             'base',
             'voice_player',
             player,
             server_id=server_id,
             volatile=True)
Esempio n. 5
0
def set_player(bot, guild_id, player):
    """Sets the voice player of the given guild."""
    data.add(bot,
             'core',
             'voice_player',
             player,
             guild_id=guild_id,
             volatile=True)
Esempio n. 6
0
async def create_client(bot):
    """Create a new wolframalpha client object and store in volatile data."""
    config = configurations.get(bot, __name__)
    client = wap.WolframAlphaEngine(config['api_key'], config['server'])
    client.ScanTimeout = config['scan_timeout']
    client.PodTimeout = config['pod_timeout']
    client.FormatTimeout = config['format_timeout']
    data.add(bot, __name__, 'client', client, volatile=True)
    data.add(bot, __name__, 'uses', {}, volatile=True)
Esempio n. 7
0
async def set_ip_address(bot, context):
    if context.arguments[0]:
        data.add(bot, __name__, 'server_ip', context.arguments[0], guild_id=context.guild.id)
        response = "IP address set!"
    else:  # Get current IP
        default_ip = configurations.get(bot, __name__, key='default_ip')
        response = "The current IP address is: {}".format(
            data.get(bot, __name__, 'server_ip', guild_id=context.guild.id, default=default_ip))
    return Response(content=response)
Esempio n. 8
0
async def autolog_setchannel(bot, context):
    """Sets the channel where logs will be dumped."""
    data.add(bot,
             __name__,
             'log_channel',
             context.arguments[0].id,
             guild_id=context.guild.id)
    return Response("The logging channel is now set to {}".format(
        context.arguments[0].mention))
Esempio n. 9
0
async def translate_default(bot, context):
    """Sets the default translation language."""
    language = context.arguments[0]
    if language:
        data.add(bot, __name__, 'default', language, guild_id=context.guild.id)
    else:
        data.remove(bot, __name__, 'default', guild_id=context.guild.id)
    return Response(
        content='Default language set to {}.'.format(language if language else 'English'))
Esempio n. 10
0
async def create_client(bot):
    """Create a new wolframalpha client object and store in volatile data."""
    config = configurations.get(bot, __name__)
    client = wap.WolframAlphaEngine(config['api_key'], config['server'])
    client.ScanTimeout = config['scan_timeout']
    client.PodTimeout = config['pod_timeout']
    client.FormatTimeout = config['format_timeout']
    data.add(bot, __name__, 'client', client, volatile=True)
    data.add(bot, __name__, 'uses', {}, volatile=True)
Esempio n. 11
0
async def autolog_messages(bot, context):
    """Sets the number of messages to log in each channel."""
    _check_log_channel(bot, context.guild)
    data.add(bot, __name__, 'message_limit', context.arguments[0], guild_id=context.guild.id)
    logged_channels = data.get(bot, __name__, 'channels', guild_id=context.guild.id, default=[])
    for channel_id in logged_channels:
        channel = context.guild.get_channel(channel_id)
        _set_logger(bot, channel)
    return Response("{} messages will be logged for each channel.".format(context.arguments[0]))
Esempio n. 12
0
async def verification_set(bot, context):
    """Sets the verification role."""
    role = context.arguments[0]
    data.add(bot,
             __name__,
             'verification_role',
             role.id,
             guild_id=context.guild.id)
    return Response(embed=discord.Embed(
        description='Verification role set to {}'.format(role.mention)))
Esempio n. 13
0
async def set_units(bot, context):
    default_units = configurations.get(bot, __name__, key='default_units')
    units = data.get(
        bot, __name__, 'server_units', guild_id=context.guild.id, default=default_units)
    if units == 'metric':
        new_units = 'nonmetric'
        response = 'US standard'
    else:
        new_units = 'metric'
        response = 'Metric'
    data.add(bot, __name__, 'server_units', new_units, guild_id=context.guild.id)
    return Response(content=response + ' units are now set as the default.')
Esempio n. 14
0
async def get_response(bot, context):

    if context.index == 0:  # Change avatar, status, or both
        if len(context.options) == 0:
            raise CBException(
                "Either the avatar, status, or both options must be used.")
        if 'avatar' in context.options:
            text = configurations.get(bot,
                                      __name__,
                                      extra='avatars',
                                      extension='txt')
            url = random.choice(text.splitlines()).rstrip()
            await _change_avatar(bot, url=url)
        if 'status' in context.options:
            text = configurations.get(bot,
                                      __name__,
                                      extra='statuses',
                                      extension='txt')
            status = random.choice(text.splitlines()).rstrip()
            try:
                await bot.change_presence(activity=discord.Game(name=status))
            except Exception as e:
                raise CBException("Failed to update the status.", e=e)

    elif context.index == 1:  # Change nickname
        try:
            await context.guild.me.edit(nick=context.arguments[0])
        except Exception as e:
            raise CBException("Failed to change the nickname.", e=e)

    elif context.index == 2:  # Change name
        if len(context.arguments[0]) > 20:
            raise CBException("Name is longer than 20 characters.")
        try:
            await bot.user.edit(username=context.arguments[0])
        except Exception as e:
            raise CBException("Failed to update the name.", e=e)

    elif context.index == 3:  # Change status
        try:
            if context.arguments[0]:
                activity = discord.Game(name=context.arguments[0])
            else:
                activity = None
            await bot.change_presence(activity=activity)
            data.add(bot, __name__, 'status', context.arguments[0])
        except Exception as e:
            raise CBException("Failed to update the status.", e=e)

    elif context.index == 4:  # Change avatar
        await _change_avatar(bot, url=context.arguments[0])

    return Response(content="Bot updated.")
Esempio n. 15
0
def add_bot_permissions(bot, plugin_name, **permissions):
    """Adds the given permissions to the bot for authentication generation."""
    dummy = discord.Permissions()
    for permission in permissions:
        try:
            getattr(dummy, permission.lower())
        except:  # Permission not found
            raise CBException("Permission '{}' does not exist".format(permission))
    current = data.get(
        bot, plugin_name, 'permissions', create=True, volatile=True)
    if current is None:
        data.add(bot, plugin_name, 'permissions', permissions, volatile=True)
Esempio n. 16
0
async def _get_advertisement_data(bot, guild, ignore_user_id=None):
    """Gets a dictionary of advertisements in the guild, or builds one if necessary.

    If ignore_user_id is provided, this will ignore the first message by that user.
    """
    rules = data.get(bot, __name__, 'rules', guild_id=guild.id)
    if not rules:
        raise CBException(
            "Commission channel rules are not configured on this server.")
    advertisement_data = data.get(bot,
                                  __name__,
                                  'advertisements',
                                  guild_id=guild.id,
                                  volatile=True)

    if advertisement_data:
        return advertisement_data

    # No data found. Fetch it manually
    channel = data.get_channel(bot, rules['channel'], safe=True)
    if not channel:
        raise CBException("The commission channel was not found.")

    # TODO: Add permission checks for channel access and deleting messages
    advertisement_data = {}
    whitelist = data.get(bot,
                         __name__,
                         'whitelist',
                         guild_id=guild.id,
                         default=[])
    async for message in channel.history(limit=100):
        author_id = message.author.id
        if (not message.author.bot
                and message.type is discord.MessageType.default
                and not message.pinned and author_id not in whitelist):
            if author_id in advertisement_data:
                logger.warn('Deleting previously undetected message %s',
                            message.id)
                await message.delete()
            else:
                if ignore_user_id == author_id:
                    ignore_user_id = None
                else:
                    advertisement_data[author_id] = message

    data.add(bot,
             __name__,
             'advertisements',
             advertisement_data,
             guild_id=guild.id,
             volatile=True)
    return advertisement_data
Esempio n. 17
0
def _update_current_game(bot, safe=False, include_setup_status=False):
    """Updates the index of the latest/current game."""
    schedule_data = data.get(bot,
                             __name__,
                             'schedule',
                             volatile=True,
                             default=[])
    current_time = datetime.datetime.utcnow()
    for index, game in enumerate(schedule_data):
        start_time, end_time = game['scheduled'], game['end']
        if start_time <= current_time < end_time:  # Update latest index
            data.add(bot, __name__, 'current_index', index, volatile=True)
            data.add(bot, __name__, 'current_game', game, volatile=True)
            if include_setup_status:
                setup_time = datetime.timedelta(seconds=game['setup_seconds'])
                return index, (current_time < start_time + setup_time)
            else:
                return index
        elif current_time < start_time:
            logger.debug(
                "The current time is less than the start time. Index: %s",
                index)
            break
    else:  # GDQ over, or past schedule
        game, index = None, 999
    if safe:
        data.add(bot, __name__, 'current_index', index, volatile=True)
        data.add(bot, __name__, 'current_game', game, volatile=True)
        if include_setup_status:
            return index, True
        else:
            return index
    raise CBException("No current game was found.")
Esempio n. 18
0
def _update_current_game(bot, safe=False, include_setup_status=False):
    """Updates the index of the latest/current game."""
    schedule_data = data.get(bot, __name__, 'schedule', volatile=True, default=[])
    current_time = datetime.datetime.utcnow()
    for index, game in enumerate(schedule_data):
        start_time, end_time = game['scheduled'], game['end']
        if start_time <= current_time < end_time:  # Update latest index
            data.add(bot, __name__, 'current_index', index, volatile=True)
            data.add(bot, __name__, 'current_game', game, volatile=True)
            if include_setup_status:
                setup_time = datetime.timedelta(seconds=game['setup_seconds'])
                return index, (current_time < start_time + setup_time)
            else:
                return index
        elif current_time < start_time:
            logger.debug("The current time is less than the start time. Index: %s", index)
            break
    else:  # GDQ over, or past schedule
        game, index = None, 999
    if safe:
        data.add(bot, __name__, 'current_index', index, volatile=True)
        data.add(bot, __name__, 'current_game', game, volatile=True)
        if include_setup_status:
            return index, True
        else:
            return index
    raise CBException("No current game was found.")
Esempio n. 19
0
def add_bot_permissions(bot, plugin_name, **permissions):
    """Adds the given permissions to the bot for authentication generation."""
    dummy = discord.Permissions()
    for permission in permissions:
        try:
            getattr(dummy, permission.lower())
        except:  # Permission not found
            raise CBException("Permission '{}' does not exist", permission)
    current = data.get(bot,
                       plugin_name,
                       'permissions',
                       create=True,
                       volatile=True)
    if current is None:
        data.add(bot, plugin_name, 'permissions', permissions, volatile=True)
Esempio n. 20
0
async def set_ip_address(bot, context):
    if context.arguments[0]:
        data.add(bot,
                 __name__,
                 'server_ip',
                 context.arguments[0],
                 guild_id=context.guild.id)
        response = "IP address set!"
    else:  # Get current IP
        default_ip = configurations.get(bot, __name__, key='default_ip')
        response = "The current IP address is: {}".format(
            data.get(bot,
                     __name__,
                     'server_ip',
                     guild_id=context.guild.id,
                     default=default_ip))
    return Response(content=response)
Esempio n. 21
0
async def check_webhook_messages(bot, message):
    """Intercepts webhook messages to the data channel.
    
    There are 3 separate stages:
    0 - Starting stage (webhook exists)
    1 - User has submitted the file, edit webhook name with return code
    2 - User acknowledges result, requests that the webhook be deleted
    """
    if message.channel != DATA_CHANNEL:
        return

    # Check for valid webhook messages
    webhook_id = message.author.id
    if webhook_id not in DATA_CHANNEL_WEBHOOK_IDS:
        return

    stage = data.get(bot, __name__, 'stage', user_id=webhook_id, volatile=True)
    if stage is not None:

        if message.content == '1' and stage == 0:  # Progress to stage 1
            owner = data.get(bot, __name__, 'owner', user_id=webhook_id, volatile=True)
            webhook = data.get(bot, __name__, 'tracker', user_id=owner.id, volatile=True)
            result = await _process_data(bot, owner, message.attachments[0].url)

            # Parse result
            data.add(bot, __name__, 'stage', 1, user_id=webhook_id, volatile=True)
            await webhook.edit(name='ok' if result == 0 else 'err:{}'.format(result))

        elif message.content == '2' and stage == 1:  # Progress to stage 2
            await _clear_webhook(bot, webhook_id)

        else:  # Invalid state progression detected (likely duplicate)
            logger.warn("Invalid state progression detected. Message content: %s", message.content)
            await _clear_webhook(bot, webhook_id)
            pass  # TODO: Consider notifying user?

    else:  # Desync

        logger.warn("Webhook state desynchronization detected.")
        await _clear_webhook(bot, webhook_id)
        webhooks = await DATA_CHANNEL.webhooks()
        for webhook in webhooks:  # In case the webhook ID was invalid
            if webhook.id == webhook_id:
                await webhook.delete()
                break
Esempio n. 22
0
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False):
    """Uploads the given file-like object to the upload channel.

    If the upload channel is specified in the configuration files, files
    will be uploaded there. Otherwise, a new server will be created, and
    used as the upload channel."""
    channel_id = configurations.get(bot, 'core', 'upload_channel')
    if not channel_id:  # Check to see if a server was already created
        channel_id = data.get(bot, 'core', 'upload_channel')
    channel = bot.get_channel(channel_id)

    if channel is None:  # Create server
        logging.debug("Creating server for upload channel...")
        try:
            server = await bot.create_server('uploads')
        except Exception as e:
            raise BotException(
                EXCEPTION,
                "Failed to create upload server. This bot is not whitelisted "
                "to create servers.",
                e=e)
        data.add(bot, 'core', 'upload_channel', server.id)
        channel = bot.get_channel(server.id)

    if channel is None:  # Shouldn't happen
        raise BotException(EXCEPTION, "Failed to get upload channel.")

    try:
        message = await bot.send_file(channel, fp, filename=filename)
        upload_url = message.attachments[0]['url']
    except Exception as e:
        raise BotException(EXCEPTION, "Failed to upload file.", e=e)

    if close:
        try:
            fp.close()
        except:
            pass
    elif rewind:
        try:
            fp.seek(0)
        except:
            pass

    return upload_url
Esempio n. 23
0
async def autolog_messages(bot, context):
    """Sets the number of messages to log in each channel."""
    _check_log_channel(bot, context.guild)
    data.add(bot,
             __name__,
             'message_limit',
             context.arguments[0],
             guild_id=context.guild.id)
    logged_channels = data.get(bot,
                               __name__,
                               'channels',
                               guild_id=context.guild.id,
                               default=[])
    for channel_id in logged_channels:
        channel = context.guild.get_channel(channel_id)
        _set_logger(bot, channel)
    return Response("{} messages will be logged for each channel.".format(
        context.arguments[0]))
Esempio n. 24
0
async def set_units(bot, context):
    default_units = configurations.get(bot, __name__, key='default_units')
    units = data.get(bot,
                     __name__,
                     'server_units',
                     guild_id=context.guild.id,
                     default=default_units)
    if units == 'metric':
        new_units = 'nonmetric'
        response = 'US standard'
    else:
        new_units = 'metric'
        response = 'Metric'
    data.add(bot,
             __name__,
             'server_units',
             new_units,
             guild_id=context.guild.id)
    return Response(content=response + ' units are now set as the default.')
Esempio n. 25
0
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False):
    """Uploads the given file-like object to the upload channel.

    If the upload channel is specified in the configuration files, files
    will be uploaded there. Otherwise, a new guild will be created, and
    used as the upload channel."""
    channel_id = configurations.get(bot, 'core', 'upload_channel')
    if not channel_id:  # Check to see if a guild was already created
        channel_id = data.get(bot, 'core', 'upload_channel')
    channel = data.get_channel(bot, channel_id, safe=True)

    # TODO: Remove. Guild creation via bots is a whitelisted process
    if channel is None:  # Create guild
        logger.debug("Creating guild for upload channel...")
        try:
            guild = await bot.create_guild('uploads')
        except Exception as e:
            raise CBException(
                "Failed to create upload guild. This bot is not whitelisted "
                "to create guilds.", e=e)
        data.add(bot, 'core', 'upload_channel', guild.id)
        channel = bot.get_channel(guild.id)

    if channel is None:  # Shouldn't happen
        raise CBException("Failed to get upload channel.")

    try:
        discord_file = discord.File(fp, filename=filename)
        message = await channel.send(file=discord_file)
        upload_url = message.attachments[0].url
    except Exception as e:
        raise CBException("Failed to upload file.", e=e)

    try:
        if close:
            fp.close()
        elif rewind:
            fp.seek(0)
    except:
        pass

    return upload_url
Esempio n. 26
0
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False):
    """Uploads the given file-like object to the upload channel.

    If the upload channel is specified in the configuration files, files
    will be uploaded there. Otherwise, a new guild will be created, and
    used as the upload channel."""
    channel_id = configurations.get(bot, 'core', 'upload_channel')
    if not channel_id:  # Check to see if a guild was already created
        channel_id = data.get(bot, 'core', 'upload_channel')
    channel = data.get_channel(bot, channel_id, safe=True)

    if channel is None:  # Create guild
        logger.debug("Creating guild for upload channel...")
        try:
            guild = await bot.create_guild('uploads')
        except Exception as e:
            raise CBException(
                "Failed to create upload guild. This bot is not whitelisted "
                "to create guilds.",
                e=e)
        data.add(bot, 'core', 'upload_channel', guild.id)
        channel = bot.get_channel(guild.id)

    if channel is None:  # Shouldn't happen
        raise CBException("Failed to get upload channel.")

    try:
        discord_file = discord.File(fp, filename=filename)
        message = await channel.send(file=discord_file)
        upload_url = message.attachments[0].url
    except Exception as e:
        raise CBException("Failed to upload file.", e=e)

    try:
        if close:
            fp.close()
        elif rewind:
            fp.seek(0)
    except:
        pass

    return upload_url
Esempio n. 27
0
async def _get_buffered_donation_stats(bot):
    """Pulls buffered donation information if it is 1 minute old or less."""
    last_pull = data.get(bot, __name__, 'last_pull', volatile=True, default=0)
    buffer_time = configurations.get(bot, __name__, 'stats_buffer_time')
    if time.time() - last_pull > buffer_time:  # Pull information
        data.add(bot, __name__, 'last_pull', time.time(), volatile=True)
        tracker_url = configurations.get(bot, __name__, 'tracker_url')
        try:
            donate_html = (await utilities.future(requests.get, tracker_url)).text
            soup = BeautifulSoup(donate_html, 'html.parser')
            donation_text = soup.find('small').text.splitlines()[1:]
            total_raised, total_donations, _unused = donation_text[1].split()
            total_donations = total_donations.strip('()')
            max_average = donation_text[3]
        except Exception as e:
            raise CBException("Failed to retrieve donation data.", e=e)
        donation_stats = [total_raised, total_donations, max_average]
        data.add(bot, __name__, 'donation_stats', donation_stats, volatile=True)
    else:
        donation_stats = data.get(bot, __name__, 'donation_stats', volatile=True)
    return donation_stats
Esempio n. 28
0
async def tagremote_start(bot, context):
    """Starts a tag remote session."""

    # Check for an existing session
    session_data = data.get(bot, __name__, 'data', guild_id=context.guild.id)
    if session_data:
        raise CBException("Session already exists.")
    if not context.channel.permissions_for(context.guild.me).manage_webhooks:
        raise CBException("Missing the `Manage Webhooks` permission.")

    # Retrieve and format tag data
    tag_dictionary = _get_tag_dictionary(bot, context.guild)

    # Check that the user is in an unblocked voice channel
    if not context.author.voice:
        raise CBException("You must be in a voice channel.")
    voice_channel = context.author.voice.channel
    await utilities.join_and_ready(bot, voice_channel, is_mod=context.elevation >= 1)

    # Create webhook
    webhook = await context.channel.create_webhook(name='Tag Remote []')

    # Upload session data
    session_code = await _upload_session_data(
        bot, context.channel, voice_channel, webhook, tag_dictionary)

    # Track session data
    session_data = {
        'webhook': webhook.id,
        'channel': context.channel.id,
        'voice_channel': voice_channel.id,
        'session': session_code
    }
    data.add(bot, __name__, 'data', session_data, guild_id=context.guild.id)
    data.list_data_append(bot, __name__, 'webhooks', webhook.id, duplicates=False)
    WEBHOOK_SET.add(webhook.id)

    return await tagremote(bot, context)
Esempio n. 29
0
def convert_core(bot, guild):
    if data.get(bot, 'core', None, guild_id=guild.id):
        logger.warn("Guild %s (%s) already had core converted", guild.name, guild.id)
        return
    base_data = data.get(bot, 'base', None, guild_id=guild.id, default={})
    if 'disabled' in base_data:
        # TODO: Iterate through toggled commands
        pass
    if 'blocked' in base_data:
        replacement = []
        for entry in base_data['blocked']:
            replacement.append(int(entry))
        base_data['blocked'] = replacement
    if 'muted_channels' in base_data:
        replacement = []
        for entry in base_data['muted_channels']:
            replacement.append(int(entry))
        base_data['muted_channels'] = replacement
    if 'moderators' in base_data:
        del base_data['moderators']
    if base_data:
        for key, value in base_data.items():
            data.add(bot, 'core', key, value, guild_id=guild.id)
        data.remove(bot, 'base', None, guild_id=guild.id)
Esempio n. 30
0
async def botowner_wrapper(bot, message, base, blueprint_index, options,
                           arguments, keywords, cleaned_content):
    response, tts, message_type, extra = ('', False, 0, None)

    if blueprint_index == 0:  # Halt
        await bot.send_message(message.channel, "Going down...")
        bot.shutdown()
    elif blueprint_index == 1:  # Restart
        await bot.send_message(message.channel, "Restarting...")
        bot.restart()
    elif blueprint_index == 2:  # Reload
        response = "Reloading..."
        message_type = 3
        extra = ('reload', )
    elif blueprint_index == 3:  # IP
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))  # Thanks Google
        ip = s.getsockname()[0]
        s.close()
        response = "Local IP: " + ip
    elif blueprint_index == 4:  # Backup
        utilities.make_backup(bot)
        await bot.send_file(message.channel,
                            '{}/temp/backup1.zip'.format(bot.path))
    elif blueprint_index == 5:  # Blacklist
        blacklist = data.get(bot, 'base', 'blacklist', default=[])
        if not arguments[0]:
            response = "Blacklisted entries: {}".format(blacklist)
        else:
            user_id = arguments[0]
            if user_id in blacklist:
                data.list_data_remove(bot, 'base', 'blacklist', user_id)
                response = "User removed from blacklist."
            else:
                data.list_data_append(bot, 'base', 'blacklist', user_id)
                response = "User added to blacklist."
    elif blueprint_index == 6:  # Toggle feedback
        status = data.get(bot, 'base', 'feedbackdisabled', default=False)
        action = "enabled" if status else "disabled"
        data.add(bot, 'base', 'feedbackdisabled', not status)
        response = "Feedback has been {}.".format(action)
    elif blueprint_index == 7:  # Announcement
        if arguments[0]:
            text = '{0}:\n{1}'.format(time.strftime('%c'), arguments[0])
            data.add(bot, 'base', 'announcement', text)
            response = "Announcement set!"
        else:
            data.add(bot, 'base', 'announcement', '')
            response = "Announcement cleared!"

    return (response, tts, message_type, extra)
Esempio n. 31
0
async def _create_session(bot, owner, editing=None):
    """Creates a session for character creation or editing"""
    webhook = await DATA_CHANNEL.create_webhook(name='ready:{}'.format(owner.id))

    # Upload data as a single file
    cursor = data.db_select(
        bot, from_arg='characters', where_arg='owner_id=%s', input_args=[owner.id])
    characters = cursor.fetchall() if cursor else []
    create_data = utilities.get_text_as_file(json.dumps({
        "version": DATA_VERSION,
        "webhook": [str(webhook.id), webhook.token],
        "existing_names": list(character.clean_name for character in characters),
        "editing": editing
    }))
    url = await utilities.upload_to_discord(bot, create_data, filename='data', close=True)
    url_segments = [it[::-1] for it in url[::-1].split('/')[2:0:-1]]  # sorry
    session_code = '{}:{}'.format(*url_segments)

    # Track webhook usage
    data.add(bot, __name__, 'tracker', webhook, user_id=owner.id, volatile=True)
    data.add(bot, __name__, 'owner', owner, user_id=webhook.id, volatile=True)
    data.add(bot, __name__, 'stage', 0, user_id=webhook.id, volatile=True)

    # Add webhook ID to global IDs
    global DATA_CHANNEL_WEBHOOK_IDS
    DATA_CHANNEL_WEBHOOK_IDS.append(webhook.id)

    # Send the owner the link
    embed = discord.Embed(
        title='Click here to access the character creator',
        url='https://jkchen2.github.io/character-template/#{}'.format(session_code),
        description='Your session code is:\n`{}`'.format(session_code))
    await owner.send(embed=embed)

    # Schedule a session timeout
    utilities.schedule(
        bot, __name__, time.time() + 7200, _session_timeout_notify,
        search=str(webhook.id), destination='u{}'.format(owner.id),
        info='Character creator session timeout')

    return session_code
Esempio n. 32
0
async def verification_set(bot, context):
    """Sets the verification role."""
    role = context.arguments[0]
    data.add(bot, __name__, 'verification_role', role.id, guild_id=context.guild.id)
    return Response(embed=discord.Embed(
        description='Verification role set to {}'.format(role.mention)))
Esempio n. 33
0
async def check_recently_deleted(bot, message):
    """Checks if a user wants to revise their last advertisement."""
    if isinstance(message.channel, discord.abc.PrivateChannel):
        return
    guild_data = data.get(bot,
                          __name__,
                          None,
                          guild_id=message.guild.id,
                          default={})
    if (not guild_data.get('rules')
            or message.channel.id != guild_data['rules']['channel']
            or message.author.id in guild_data.get('whitelist', [])
            or message.author.bot):
        return

    cooldown = guild_data['rules']['cooldown']
    message_time = message.created_at.replace(tzinfo=tz.utc).timestamp()
    advertisement_data = await _get_advertisement_data(bot, message.guild)
    author_id = message.author.id

    # Message mismatch. Ignore deletion
    if advertisement_data.get(author_id) != message:
        return

    # User wants to replace their last message (limit within 10 minutes)
    future_message = await message.channel.history(limit=1,
                                                   after=message).flatten()
    time_delta = time.time() - message_time
    if not future_message and time_delta < 60:
        del advertisement_data[author_id]
        utilities.remove_schedule_entries(bot,
                                          __name__,
                                          search='c_ad_{}'.format(
                                              message.guild.id),
                                          destination='u{}'.format(author_id))
        notification = (
            'Heads up, you have deleted your last advertisement within 10 minutes of posting it '
            '(and nobody else posted an advertisement during that time).\n\n'
            'You can submit a revised advertisement now if you wish.')
        await message.author.send(embed=discord.Embed(description=notification)
                                  )

    # User deleted their advertisement for some reason?
    # Keep message creation time to prevent users from circumventing the cooldown
    elif time_delta < cooldown:
        deleted_persistence = data.get(bot,
                                       __name__,
                                       'recently_deleted',
                                       guild_id=message.guild.id,
                                       default={})

        # Clear any expired entries
        to_remove = [
            k for k, v in deleted_persistence.items()
            if time.time() - v > cooldown
        ]
        for remove_id in to_remove:
            del deleted_persistence[str(remove_id)]

        # Add persistence entry
        deleted_persistence[str(author_id)] = message_time
        data.add(bot,
                 __name__,
                 'recently_deleted',
                 deleted_persistence,
                 guild_id=message.guild.id)
Esempio n. 34
0
def set_player(bot, guild_id, player):
    """Sets the voice player of the given guild."""
    data.add(bot, 'core', 'voice_player', player, guild_id=guild_id, volatile=True)
Esempio n. 35
0
async def owner_wrapper(bot, message, base, blueprint_index, options,
                        arguments, keywords, cleaned_content):
    response, tts, message_type, extra = ('', False, 0, None)
    mod_action = ''

    send_notifications = data.get(bot,
                                  'base',
                                  'notifications',
                                  server_id=message.server.id,
                                  default=True)

    if blueprint_index in (0, 1):  # Add or remove moderator
        user = data.get_member(bot, arguments[0], server=message.server)
        user_is_mod = data.is_mod(bot, message.server, user.id, strict=True)
        user_is_elevated = data.is_mod(bot, message.server, user.id)
        blocked = data.is_blocked(bot, message.server, user.id, strict=True)
        mod_action = 'Added {}' if blueprint_index == 0 else 'Removed {}'
        mod_action = mod_action.format(
            '{0} ({0.id}) as a moderator'.format(user))
        if blocked:
            raise BotException(EXCEPTION, "User is blocked.")
        elif blueprint_index == 0:  # add
            if user_is_mod or user_is_elevated:
                raise BotException(EXCEPTION, "User is already a moderator.")
            else:
                data.list_data_append(bot,
                                      'base',
                                      'moderators',
                                      user.id,
                                      server_id=message.server.id)
                response = "User is now a moderator."
        else:  # remove
            if not user_is_mod:
                raise BotException(EXCEPTION,
                                   "User is not in the moderators list.")
            else:
                data.list_data_remove(bot,
                                      'base',
                                      'moderators',
                                      user.id,
                                      server_id=message.server.id)
                response = "User is no longer a moderator."

    elif blueprint_index == 2:  # Send feedback
        if data.get(bot, 'base', 'feedbackdisabled', default=False):
            response = ("Feedback has been temporarily disabled, probably "
                        "due to some troll spammers.")
        else:
            text = arguments[0]
            if len(text) > 1500:
                raise BotException(
                    EXCEPTION, "Whoa! That's a lot of feedback. "
                    "1500 characters or fewer, please.")
            text = ('{0} ({0.id}) on {1.timestamp}:'
                    '\n\t{2}').format(message.author, message, text)
            await utilities.notify_owners(bot, text, user_id=message.author.id)
            response = "Message sent to bot owners."

    elif blueprint_index == 3:  # Toggle notifications
        response = ("Bot moderator activity notifications are now turned "
                    "{}").format("OFF." if send_notifications else "ON.")
        data.add(bot,
                 'base',
                 'notifications',
                 not send_notifications,
                 server_id=message.server.id)

    # Send notification if configured
    if mod_action and send_notifications:
        if message.edited_timestamp:
            timestamp = message.edited_timestamp
        else:
            timestamp = message.timestamp
        notification = 'From {0.server} on {1}, you:\n\t{2}'.format(
            message.author, timestamp, mod_action)
        logs = await utilities.get_log_text(bot,
                                            message.channel,
                                            limit=20,
                                            before=message)
        logs += '\n{}'.format(utilities.get_formatted_message(message))
        await bot.send_message(message.server.owner, notification)
        await utilities.send_text_as_file(bot, message.server.owner, logs,
                                          'context')

    return (response, tts, message_type, extra)
Esempio n. 36
0
async def mod_wrapper(bot, message, base, blueprint_index, options, arguments,
                      keywords, cleaned_content):
    response, tts, message_type, extra = ('', False, 0, None)
    mod_action = ''

    if blueprint_index == 0:  # info
        server_data = data.get(bot,
                               'base',
                               None,
                               server_id=message.server.id,
                               default={})
        disabled_commands = server_data.get('disabled', [])
        display_list = []
        for disabled_command in disabled_commands:
            display_list.append('{0} ({1})'.format(
                disabled_command[0],
                'all' if disabled_command[1] == -1 else disabled_command[1] +
                1))
        response = ('```\n'
                    'Information for server {0}\n'
                    'ID: {0.id}\n'
                    'Owner: {0.owner.id}\n'
                    'Moderators: {1}\n'
                    'Blocked users: {2}\n'
                    'Muted: {3}\n'
                    'Muted channels: {4}\n'
                    'Command invoker: {5}\n'
                    'Mention mode: {6}\n'
                    'Disabled commands: {7}```').format(
                        message.server, server_data.get('moderators', []),
                        server_data.get('blocked', []),
                        server_data.get('muted', []),
                        server_data.get('muted_channels', []),
                        server_data.get('command_invoker', None),
                        server_data.get('mention_mode', False), display_list)

    elif blueprint_index == 1:  # Toggle command
        try:  # Explicit index
            split_arguments = arguments[0].split()
            command = bot.commands[split_arguments[0]]
            guess = [command.base, int(split_arguments[1]) - 1]
            assert -1 < guess[1] < len(command.blueprints)
        except IndexError:  # No index
            guess = [command.base, -1]
        except:  # Guess the help index
            guess = list(parser.guess_index(bot, arguments[0]))
        if guess[0] is None:
            raise BotException(EXCEPTION, "Invalid base.")

        command = bot.commands[guess[0]]
        if command.plugin is bot.commands['base'].plugin:
            raise BotException(EXCEPTION,
                               "The base commands cannot be disabled.")
        pass_in = (bot, 'base', 'disabled', guess)
        pass_in_keywords = {'server_id': message.server.id}
        disabled_commands = data.get(*pass_in[:-1],
                                     **pass_in_keywords,
                                     default=[])
        if guess in disabled_commands:
            data.list_data_remove(*pass_in, **pass_in_keywords)
            response = "Enabled"
        else:
            data.list_data_append(*pass_in, **pass_in_keywords)
            response = "Disabled"
        response += " the `{0}` command {1}.".format(
            guess[0], "and all associated subcommands"
            if guess[1] == -1 else "(subcommand {})".format(guess[1] + 1))
        mod_action = response

    elif blueprint_index in (2, 3):  # Block or unblock
        user = data.get_member(bot, arguments[0], message.server)
        block = blueprint_index == 2
        mod_action = 'Blocked {}' if block else 'Unblocked {}'
        mod_action = mod_action.format('{0} ({0.id})'.format(user))
        blocked = data.is_blocked(bot, message.server, user.id, strict=True)
        mod = data.is_mod(bot, message.server, user.id)
        if mod:
            raise BotException(EXCEPTION,
                               "Cannot block or unblock a moderator.")
        elif block:
            if blocked:
                raise BotException(EXCEPTION, "User is already blocked.")
            else:
                data.list_data_append(bot,
                                      'base',
                                      'blocked',
                                      user.id,
                                      server_id=message.server.id)
                response = "User is now blocked."
        else:
            if not blocked:
                raise BotException(EXCEPTION, "User is already unblocked.")
            else:
                data.list_data_remove(bot,
                                      'base',
                                      'blocked',
                                      user.id,
                                      server_id=message.server.id)
                response = "User is now unblocked."

    elif blueprint_index == 4:  # Clear
        response = ('​' + '\n' * 80 +
                    "The chat was pushed up by a bot moderator.")

    elif blueprint_index in (5, 6):  # Mute or unmute
        server_id = message.server.id
        mute = blueprint_index == 5
        mod_action = 'Muted {}' if mute else 'Unmuted {}'

        if arguments[0]:
            channel = data.get_channel(bot, arguments[0], message.server)
            muted = channel.id in data.get(bot,
                                           'base',
                                           'muted_channels',
                                           server_id=server_id,
                                           default=[])
            mod_action = mod_action.format(channel.name)
            if mute:
                if muted:
                    raise BotException(EXCEPTION, "Channel is already muted.")
                else:
                    data.list_data_append(bot,
                                          'base',
                                          'muted_channels',
                                          channel.id,
                                          server_id=server_id)
                    if str(channel.type) == 'voice':  # disconnect
                        await utilities.leave_and_stop(bot, message.server)
                    response = "Channel muted."
            else:  # unmute
                if not muted:
                    raise BotException(EXCEPTION,
                                       "Channel is already unmuted.")
                else:
                    data.list_data_remove(bot,
                                          'base',
                                          'muted_channels',
                                          channel.id,
                                          server_id=server_id)
                    response = "Channel unmuted."

        else:  # server
            mod_action = mod_action.format('the server')
            muted = data.get(bot,
                             'base',
                             'muted',
                             server_id=server_id,
                             default=False)
            if not (muted ^ mute):
                response = "Server is already {}muted.".format(
                    '' if muted else 'un')
                raise BotException(EXCEPTION, response)
            else:
                data.add(bot, 'base', 'muted', mute, server_id=server_id)
                response = "Server {}muted.".format('' if mute else 'un')

    elif blueprint_index == 7:  # Invoker
        if len(arguments[0]) > 10:
            raise BotException(
                EXCEPTION,
                "The invoker can be a maximum of 10 characters long.")
        data.add(bot,
                 'base',
                 'command_invoker',
                 arguments[0] if arguments[0] else None,
                 server_id=message.server.id)
        response = "Custom command invoker {}.".format(
            'set' if arguments[0] else 'cleared')
        if arguments[0]:
            response = "Custom command invoker set."
            mod_action = "Set the server command invoker to '{}'.".format(
                arguments[0])
        else:
            response = "Custom command invoker cleared."
            mod_action = "Removed the custom command invoker."

    elif blueprint_index == 8:  # Mention
        current_mode = data.get(bot,
                                'base',
                                'mention_mode',
                                server_id=message.server.id,
                                default=False)
        data.add(bot,
                 'base',
                 'mention_mode',
                 not current_mode,
                 server_id=message.server.id)
        response = "Mention mode {}activated.".format(
            'de' if current_mode else '')
        mod_action = "{}activated mention mode.".format(
            'de' if current_mode else '').capitalize()

    # Send notification if configured
    send_notifications = data.get(bot,
                                  'base',
                                  'notifications',
                                  server_id=message.server.id,
                                  default=True)
    if mod_action and send_notifications:
        if message.edited_timestamp:
            timestamp = message.edited_timestamp
        else:
            timestamp = message.timestamp
        notification = ('Moderator {0} ({0.id}) from {0.server} on {1}:\n\t'
                        '{2}').format(message.author, timestamp, mod_action)
        logs = await utilities.get_log_text(bot,
                                            message.channel,
                                            limit=20,
                                            before=message)
        logs += '\n{}'.format(utilities.get_formatted_message(message))
        await bot.send_message(message.server.owner, notification)
        await utilities.send_text_as_file(bot, message.server.owner, logs,
                                          'context')

    return (response, tts, message_type, extra)
Esempio n. 37
0
async def _violation_notification(bot, message, awoo_tier, send_message=True):
    """
    Logs the violation and (optionally) sends the user a notification.

    Standard notification: once per violation, up to 1 time
    None: 2 violations
    Silence notification: 1 violation

    Reset period for notifications is 1 minute.

    Stress indicates a number of users making a violation within a 60 second period.
    Tier 1: 3 members
    Tier 2: 5 members
    Tier 3: 8 members
    """

    author, channel = message.author, message.channel
    current_time = time.time()
    violation_data = data.get(bot,
                              __name__,
                              'user_violation',
                              user_id=author.id,
                              volatile=True)
    channel_violation_data = data.get(bot,
                                      __name__,
                                      'channel_violation',
                                      channel_id=channel.id,
                                      volatile=True)
    if not violation_data or current_time - violation_data['time'] >= 60:
        violation_data = {'time': 0, 'violations': 0}
        data.add(bot,
                 __name__,
                 'user_violation',
                 violation_data,
                 user_id=author.id,
                 volatile=True)
    if not channel_violation_data or current_time - channel_violation_data[
            'time'] >= 60:
        channel_violation_data = {
            'time': 0,
            'violators': set(),
            'sent_tier': 0
        }
        data.add(bot,
                 __name__,
                 'channel_violation',
                 channel_violation_data,
                 channel_id=channel.id,
                 volatile=True)
    violation_data['violations'] += 1
    violation_data['time'] = current_time
    channel_violation_data['violators'].add(author.id)
    channel_violation_data['time'] = current_time

    # Update table
    set_arg = 'debt = debt+%s, violations = violations+1'
    if awoo_tier == 2:
        set_arg += ', sneaky = sneaky+1'
    cursor = data.db_select(bot,
                            from_arg='awoo',
                            where_arg='user_id=%s',
                            input_args=[author.id])
    entry = cursor.fetchone() if cursor else None
    if entry:
        data.db_update(bot,
                       'awoo',
                       set_arg=set_arg,
                       where_arg='user_id=%s',
                       input_args=[fine, author.id])
    else:
        data.db_insert(
            bot,
            'awoo',
            input_args=[author.id, fine, 1, 1 if awoo_tier == 2 else 0])

    # Add a snarky message depending on the tier
    if awoo_tier == 2:  # Attempted bypass
        snark = random.choice(statements['bypass']) + '\n'
    elif awoo_tier == 3:  # Legalization plea
        snark = random.choice(statements['legalize']) + '\n'
    else:
        snark = ''

    # Notify user
    logger.debug("Violations: %s", violation_data['violations'])
    text = ''
    if violation_data['violations'] <= 1:
        text = "{}{} has been fined ${} for an awoo violation.".format(
            snark, author.mention, fine)
    elif violation_data['violations'] == 4:
        text = "{} {}".format(author.mention,
                              random.choice(statements['silence']))
    elif awoo_tier == 3 and violation_data[
            'violations'] <= 3:  # Legalization plea, but silent
        text = snark
    if send_message and text:
        await channel.send(content=text)
    else:
        await message.add_reaction(
            random.choice(['🚩', '🛑', '�', '⛔', '🚫']))

    # Stress
    violators, sent_tier = channel_violation_data[
        'violators'], channel_violation_data['sent_tier']
    if (len(violators) == 3 and sent_tier == 0
            or len(violators) == 5 and sent_tier == 1
            or len(violators) == 8 and sent_tier == 2):
        if send_message:
            await message.channel.send(
                random.choice(statements['stress'][sent_tier]))
        channel_violation_data['sent_tier'] += 1
Esempio n. 38
0
async def autolog_setchannel(bot, context):
    """Sets the channel where logs will be dumped."""
    data.add(bot, __name__, 'log_channel', context.arguments[0].id, guild_id=context.guild.id)
    return Response("The logging channel is now set to {}".format(context.arguments[0].mention))
Esempio n. 39
0
async def _violation_notification(bot, message, awoo_tier, send_message=True):
    """
    Logs the violation and (optionally) sends the user a notification.

    Standard notification: once per violation, up to 1 time
    None: 2 violations
    Silence notification: 1 violation

    Reset period for notifications is 1 minute.

    Stress indicates a number of users making a violation within a 60 second period.
    Tier 1: 3 members
    Tier 2: 5 members
    Tier 3: 8 members
    """

    author, channel = message.author, message.channel
    current_time = time.time()
    violation_data = data.get(
        bot, __name__, 'user_violation', user_id=author.id, volatile=True)
    channel_violation_data = data.get(
        bot, __name__, 'channel_violation', channel_id=channel.id, volatile=True)
    if not violation_data or current_time - violation_data['time'] >= 60:
        violation_data = {'time': 0, 'violations': 0}
        data.add(bot, __name__, 'user_violation', violation_data, user_id=author.id, volatile=True)
    if not channel_violation_data or current_time - channel_violation_data['time'] >= 60:
        channel_violation_data = {'time': 0, 'violators': set(), 'sent_tier': 0}
        data.add(
            bot, __name__, 'channel_violation', channel_violation_data,
            channel_id=channel.id, volatile=True)
    violation_data['violations'] += 1
    violation_data['time'] = current_time
    channel_violation_data['violators'].add(author.id)
    channel_violation_data['time'] = current_time

    # Update table
    set_arg = 'debt = debt+%s, violations = violations+1'
    if awoo_tier == 2:
        set_arg += ', sneaky = sneaky+1'
    cursor = data.db_select(bot, from_arg='awoo', where_arg='user_id=%s', input_args=[author.id])
    entry = cursor.fetchone() if cursor else None
    if entry:
        data.db_update(
            bot, 'awoo', set_arg=set_arg, where_arg='user_id=%s', input_args=[fine, author.id])
    else:
        data.db_insert(bot, 'awoo', input_args=[author.id, fine, 1, 1 if awoo_tier == 2 else 0])

    # Add a snarky message depending on the tier
    if awoo_tier == 2:  # Attempted bypass
        snark = random.choice(statements['bypass']) + '\n'
    elif awoo_tier == 3:  # Legalization plea
        snark = random.choice(statements['legalize']) + '\n'
    else:
        snark = ''

    # Notify user
    logger.debug("Violations: %s", violation_data['violations'])
    text = ''
    if violation_data['violations'] <= 1:
        text = "{}{} has been fined ${} for an awoo violation.".format(snark, author.mention, fine)
    elif violation_data['violations'] == 4:
        text = "{} {}".format(author.mention, random.choice(statements['silence']))
    elif awoo_tier == 3 and violation_data['violations'] <= 3:  # Legalization plea, but silent
        text = snark
    if send_message and text:
        await channel.send(content=text)
    else:
        await message.add_reaction(random.choice(['🚩', '🛑', '�', '⛔', '🚫']))

    # Stress
    violators, sent_tier = channel_violation_data['violators'], channel_violation_data['sent_tier']
    if (len(violators) == 3 and sent_tier == 0 or
            len(violators) == 5 and sent_tier == 1 or
            len(violators) == 8 and sent_tier == 2):
        if send_message:
            await message.channel.send(random.choice(statements['stress'][sent_tier]))
        channel_violation_data['sent_tier'] += 1
Esempio n. 40
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
Esempio n. 41
0
async def _set_debug_weeks(bot, weeks):
    """Change the week delta programmatically for testing purposes."""
    data.add(bot, __name__, 'debug_weeks', int(weeks), volatile=True)
    await _update_schedule(bot)
    return "Schedule updated."
Esempio n. 42
0
async def _set_debug_weeks(bot, weeks):
    """Change the week delta programmatically for testing purposes."""
    data.add(bot, __name__, 'debug_weeks', int(weeks), volatile=True)
    await _update_schedule(bot)
    return "Schedule updated."
Esempio n. 43
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