コード例 #1
0
async def awoo_leaderboard(bot, context):
    """Displays the top 10 violators."""
    cursor = data.db_select(bot,
                            from_arg='awoo',
                            additional='ORDER BY debt DESC',
                            limit=10)
    entries = cursor.fetchall() if cursor else []
    if not entries:
        raise CBException("Nobody has made any awoo violations yet!")

    stats = [[], []]  # debt/violations, user
    for index, entry in enumerate(entries):
        stats[0].append('`{0}.` ${1.debt} | {1.violations}'.format(
            index + 1, entry))
        user = data.get_member(bot,
                               entry.user_id,
                               safe=True,
                               attribute='mention')
        user = user or 'Unknown ({})'.format(entry.user_id)
        stats[1].append('`\u200b`{}'.format(user))

    embed = discord.Embed(title=':scales: Awoo violation leaderboard')
    embed.add_field(name='Debt | Violations', value='\n'.join(stats[0]))
    embed.add_field(name='User', value='\n'.join(stats[1]))
    return Response(embed=embed)
コード例 #2
0
def setup_characters_table(bot):
    """Creates the characters table and updates outdated entries."""
    data.db_create_table(bot, 'characters', template='characters_template')
    cursor = data.db_execute(
        bot, 'SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s',
        input_args=['characters'])
    columns = [it[0] for it in cursor.fetchall()]
    if 'tags' not in columns:
        data.db_execute(bot, 'ALTER TABLE characters ADD COLUMN tags text[]')
    if 'modified' not in columns:
        data.db_execute(bot, 'ALTER TABLE characters ADD COLUMN modified timestamp')

    name_index = 'IX_character_order'
    if not data.db_exists(bot, name_index):
        data.db_execute(bot, 'CREATE INDEX {} ON characters (clean_name ASC)'.format(name_index))

    # Select all entries and convert
    cursor = data.db_select(bot, from_arg='characters')
    for entry in cursor.fetchall():
        if entry.data['version'] == 1:  # NOTE: Change per version bump
            dt = datetime.datetime.utcfromtimestamp(entry.data['created'])
            new_data = entry.data
            tags = [new_data['type'], entry.clean_name]
            new_data['attribute_order'] = list(new_data['attributes'].keys())
            new_data['images'] = [[it, '', ''] for it in new_data['images']]
            new_data['tags'] = tags
            new_data['version'] = DATA_VERSION
            data.db_update(
                bot, 'characters', set_arg='data=%s, tags=%s, modified=%s',
                where_arg='clean_name=%s AND owner_id=%s',
                input_args=(Json(new_data), tags, dt, entry.clean_name, entry.owner_id))
コード例 #3
0
async def character_create(bot, context):
    """Creates a new character entry."""

    # Check if an entry is currently being created/edited
    tracker = data.get(bot, __name__, 'tracker', user_id=context.author.id, volatile=True)
    if tracker:
        return Response(
            content=(
                "You are currently already creating or editing a character entry. "
                "Would you like to cancel your current session?"),
            message_type=MessageTypes.INTERACTIVE,
            extra_function=_cancel_menu,
            extra={'buttons': ['🇾', '🇳']})

    # 10 character limit
    cursor = data.db_select(
        bot, from_arg='characters', where_arg='owner_id=%s', input_args=[context.author.id])
    characters = cursor.fetchall() if cursor else []
    if len(characters) >= 10:
        raise CBException("Cannot create more than 10 characters.")

    # Use the provided character file
    if context.message.attachments:
        content = await _process_data(
            bot, context.author, context.message.attachments[0].url, propagate_error=True)
        return Response(content=content)

    # Use the online entry creator
    else:
        await _create_session(bot, context.author)
        if not context.direct:
            await context.message.add_reaction('📨')
コード例 #4
0
ファイル: utilities.py プロジェクト: sasma/JshBot
def get_schedule_entries(bot,
                         plugin_name,
                         search=None,
                         destination=None,
                         custom_match=None,
                         custom_args=[]):
    """Gets the entries given the search or match arguments."""
    if custom_match:
        where_arg = custom_match
        input_args = custom_args
    else:
        where_arg = 'plugin = %s'
        input_args = [plugin_name]
        if search is not None:
            where_arg += ' AND search = %s'
            input_args.append(search)
        if destination is not None:
            where_arg += ' AND destination = %s'
            input_args.append(destination)

    cursor = data.db_select(bot,
                            from_arg='schedule',
                            where_arg=where_arg,
                            additional='ORDER BY time ASC',
                            input_args=input_args,
                            safe=False)
    entries = cursor.fetchall()
    converted = []
    for entry in entries:
        if entry[3]:
            payload = json.loads(entry[3])
        else:
            payload = entry[3]
        converted.append(entry[:3] + (payload, ) + entry[4:])
    return converted
コード例 #5
0
async def character_browse(bot, context):
    """Browses a list of all characters."""
    character_listing = data.db_select(
        bot, from_arg='characters', additional='ORDER BY clean_name ASC').fetchall()
    if not character_listing:
        raise CBException("There are no characters in the database.")
    page_index = 0
    if context.arguments[0]:
        search = context.arguments[0]
        closest_index = 0
        for index, character in enumerate(character_listing):
            if search <= character.clean_name:
                closest_index = index
            else:
                break
        page_index = int(closest_index / 10)  # 10 entries per page
    embed = discord.Embed(
        title=':book: Character browser', description='{} total character{}'.format(
            len(character_listing), '' if len(character_listing) == 1 else 's'))
    state_data = [page_index, character_listing]
    return Response(
        embed=_build_browser_menu(bot, embed, *state_data),
        message_type=MessageTypes.INTERACTIVE,
        extra_function=_browser_menu,
        extra={'buttons': ['⬅', '➡'], 'userlock': False},
        state_data=state_data)
コード例 #6
0
def _user_character_search(bot, command_author, owner=None, character_search=None):
    """Finds characters under the given owner, search, or both."""
    # Setup select arguments
    where_args, input_args = [], []
    if not (owner or character_search):
        where_args.append('owner_id=%s')
        input_args.append(command_author.id)
    if owner:
        where_args.append('owner_id=%s')
        input_args.append(owner.id)
    if character_search:
        where_args.append('clean_name=%s')
        input_args.append(character_search)

    # Get character list
    cursor = data.db_select(
        bot, from_arg='characters', where_arg=' AND '.join(where_args), input_args=input_args)
    characters = cursor.fetchall() if cursor else []
    if not characters:
        if owner:
            cursor = data.db_select(
                bot, from_arg='characters', where_arg='owner_id=%s', input_args=[owner.id])
            owner_characters = cursor.fetchall() if cursor else []
            if owner_characters:
                raise CBException(
                    "{} has no character named \"{}\".".format(
                        owner.mention, character_search))
            else:
                raise CBException("{} has no character entries.".format(owner.mention))
        elif character_search:
            raise CBException("No character named \"{}\" was found.".format(character_search))
        else:
            raise CBException(
                "You have no character entries!\n"
                "You can create one with `{}character create`".format(
                    utilities.get_invoker(bot, getattr(command_author, 'guild', None))))

    # Check if character list contains characters made by the command author
    character_index = 0
    if not owner:
        for index, character in enumerate(characters):
            if character.owner_id == command_author.id:
                character_index = index
                break

    return [character_index, characters]
コード例 #7
0
async def list_text(bot, context):
    list_lines = []
    for text_type in TYPE_NAMES:
        list_lines.append('\n\n' + text_type)
        cursor = data.db_select(bot, from_arg='txyz_' + text_type)
        for key, value in cursor.fetchall():
            list_lines.append('\t{}: {}'.format(key, value))
    text_file = utilities.get_text_as_file('\n'.join(list_lines))
    discord_file = discord.File(text_file, 'txyz_list.txt')
    return Response(content='Table contents:', file=discord_file)
コード例 #8
0
ファイル: txyz.py プロジェクト: jkchen2/JshBot-plugins
async def list_text(bot, context):
    list_lines = []
    for text_type in TYPE_NAMES:
        list_lines.append('\n\n' + text_type)
        cursor = data.db_select(bot, from_arg='txyz_' + text_type)
        for key, value in cursor.fetchall():
            list_lines.append('\t{}: {}'.format(key, value))
    text_file = utilities.get_text_as_file('\n'.join(list_lines))
    discord_file = discord.File(text_file, 'txyz_list.txt')
    return Response(content='Table contents:', file=discord_file)
コード例 #9
0
 def __call__(self, bot, message, value, *a):
     if len(value) > 50:
         raise CBException("Names cannot be longer than 50 characters.")
     clean_name = _clean_text_wrapper(value)
     cursor = data.db_select(
         bot, from_arg='characters', where_arg='clean_name=%s AND owner_id=%s',
         input_args=(clean_name, message.author.id))
     match = cursor.fetchone()
     if not match:
         raise CBException("You don't have a character by that name.")
     return Character(message.author, match.data, match.tags)
コード例 #10
0
ファイル: no_awoo.py プロジェクト: jkchen2/JshBot-plugins
async def awoo_stats(bot, context):
    """Pulls stats on the given user."""
    user = context.arguments[0] or context.author
    cursor = data.db_select(bot, from_arg='awoo', where_arg='user_id=%s', input_args=[user.id])
    entry = cursor.fetchone() if cursor else None
    if not entry:
        raise CBException(
            '{} has not made a single awoo violation. What a good cookie.'.format(user.mention))
    embed = discord.Embed(title=':scales: Awoo violation statistics', description=user.mention)
    embed.add_field(name='Debt', value='${}'.format(entry.debt))
    embed.add_field(name='Violations', value='{}'.format(entry.violations))
    return Response(embed=embed)
コード例 #11
0
ファイル: txyz.py プロジェクト: jkchen2/JshBot-plugins
async def _cycle_specific(bot, cycle_type):
    table_name = 'txyz_' + TYPE_NAMES[cycle_type]
    cursor = data.db_select(bot, from_arg=table_name, additional='ORDER BY RANDOM()', limit=1)
    result = cursor.fetchone()
    if result:
        result = result[1]
    else:
        result = DEFAULTS[cycle_type]
    txyz_guild = bot.get_guild(TXYZ_GUILD)
    selected_channel = txyz_guild.voice_channels[cycle_type]
    await selected_channel.edit(name='_' + result)
    logger.debug('New %s: %s', table_name[5:-1], result)
    return result
コード例 #12
0
ファイル: utilities.py プロジェクト: jkchen2/JshBot
async def _start_scheduler(bot):
    """Starts the interal scheduler."""
    await bot.wait_until_ready()
    if bot.schedule_timer:  # Scheduler already running
        bot.schedule_timer.cancel()
        bot.schedule_timer = None
    cursor = data.db_select(
        bot, from_arg='schedule', additional='ORDER BY time ASC', limit=1, safe=False)
    result = cursor.fetchone()
    if result:
        delta = result.time - time.time()
        logger.debug("Starting scheduled event %s", result.id)
        bot.schedule_timer = asyncio.ensure_future(_schedule_timer(bot, result, delta))
    else:
        logger.debug("No pending scheduled event available.")
コード例 #13
0
async def _cycle_specific(bot, cycle_type):
    table_name = 'txyz_' + TYPE_NAMES[cycle_type]
    cursor = data.db_select(bot,
                            from_arg=table_name,
                            additional='ORDER BY RANDOM()',
                            limit=1)
    result = cursor.fetchone()
    if result:
        result = result[1]
    else:
        result = DEFAULTS[cycle_type]
    txyz_guild = bot.get_guild(TXYZ_GUILD)
    selected_channel = txyz_guild.voice_channels[cycle_type]
    await selected_channel.edit(name='_' + result)
    logger.debug('New %s: %s', table_name[5:-1], result)
    return result
コード例 #14
0
async def awoo_stats(bot, context):
    """Pulls stats on the given user."""
    user = context.arguments[0] or context.author
    cursor = data.db_select(bot,
                            from_arg='awoo',
                            where_arg='user_id=%s',
                            input_args=[user.id])
    entry = cursor.fetchone() if cursor else None
    if not entry:
        raise CBException(
            '{} has not made a single awoo violation. What a good cookie.'.
            format(user.mention))
    embed = discord.Embed(title=':scales: Awoo violation statistics',
                          description=user.mention)
    embed.add_field(name='Debt', value='${}'.format(entry.debt))
    embed.add_field(name='Violations', value='{}'.format(entry.violations))
    return Response(embed=embed)
コード例 #15
0
ファイル: no_awoo.py プロジェクト: jkchen2/JshBot-plugins
async def awoo_leaderboard(bot, context):
    """Displays the top 10 violators."""
    cursor = data.db_select(bot, from_arg='awoo', additional='ORDER BY debt DESC', limit=10)
    entries = cursor.fetchall() if cursor else []
    if not entries:
        raise CBException("Nobody has made any awoo violations yet!")

    stats = [[], []]  # debt/violations, user
    for index, entry in enumerate(entries):
        stats[0].append('`{0}.` ${1.debt} | {1.violations}'.format(index + 1, entry))
        user = data.get_member(bot, entry.user_id, safe=True, attribute='mention')
        user = user or 'Unknown ({})'.format(entry.user_id)
        stats[1].append('`\u200b`{}'.format(user))

    embed = discord.Embed(title=':scales: Awoo violation leaderboard')
    embed.add_field(name='Debt | Violations', value='\n'.join(stats[0]))
    embed.add_field(name='User', value='\n'.join(stats[1]))
    return Response(embed=embed)
コード例 #16
0
async def character_search(bot, context):
    """Searches for characters with the given list of tags."""
    tags = [_clean_text_wrapper(it) for it in context.arguments]
    cursor = data.db_select(
        bot, from_arg='characters', where_arg='tags @> %s',
        input_args=[tags], additional='ORDER BY clean_name ASC')
    character_listing = cursor.fetchall() if cursor else []
    if not character_listing:
        raise CBException("No characters found matching those tags.")
    embed = discord.Embed(
        title=':book: Character search', description='{} character{} matching: #{}'.format(
            len(character_listing), '' if len(character_listing) == 1 else 's', ' #'.join(tags)))
    state_data = [0, character_listing]
    return Response(
        embed=_build_browser_menu(bot, embed, *state_data),
        message_type=MessageTypes.INTERACTIVE,
        extra_function=_browser_menu,
        extra={'buttons': ['⬅', '➡'], 'userlock': False},
        state_data=state_data)
コード例 #17
0
ファイル: utilities.py プロジェクト: sasma/JshBot
async def _start_scheduler(bot):
    """Starts the interal scheduler."""
    if bot.schedule_timer:  # Scheduler already running
        bot.schedule_timer.cancel()
        bot.schedule_timer = None
    cursor = data.db_select(bot,
                            from_arg='schedule',
                            additional='ORDER BY time ASC',
                            limit=1,
                            safe=False)
    result = cursor.fetchone()
    if result:
        delta = result[0] - time.time()
        logger.debug("_start_scheduler is starting a scheduled event.")
        bot.schedule_timer = asyncio.ensure_future(
            _schedule_timer(bot, result, delta))
    else:
        logger.debug(
            "_start_scheduler could not find a pending scheduled event.")
コード例 #18
0
ファイル: utilities.py プロジェクト: jkchen2/JshBot
def get_schedule_entries(
        bot, plugin_name, search=None, destination=None, custom_match=None, custom_args=[]):
    """Gets the entries given the search or match arguments."""
    if custom_match:
        where_arg = custom_match
        input_args = custom_args
    else:
        where_arg = 'plugin = %s'
        input_args = [plugin_name]
        if search is not None:
            where_arg += ' AND search = %s'
            input_args.append(search)
        if destination is not None:
            where_arg += ' AND destination = %s'
            input_args.append(destination)

    cursor = data.db_select(
        bot, from_arg='schedule', where_arg=where_arg,
        additional='ORDER BY time ASC', input_args=input_args, safe=False)
    return cursor.fetchall()
コード例 #19
0
def _get_tag_dictionary(bot, guild):
    """Retrieves the tag dictionary of the server."""
    if configurations.get(bot, 'tags.py', 'global_tags'):
        table_suffix = 'global'
    else:
        table_suffix = str(guild.id)
    tags_plugin = bot.plugins['tags.py']
    sound_bit = tags_plugin._get_flag_bits(['sound'])
    private_bit = tags_plugin._get_flag_bits(['private'])
    cursor = data.db_select(
        bot, from_arg='tags', table_suffix=table_suffix,
        where_arg='flags & %s = %s AND flags & %s = 0',
        input_args=[sound_bit, sound_bit, private_bit])
    raw_tag_list = cursor.fetchall() if cursor else []
    if not raw_tag_list:
        raise CBException("No sound tags available.")
    tag_dictionary = {}
    for tag in raw_tag_list:
        tag_dictionary[tag.key] = {'name': tag.name, 'hits': tag.hits}
    return tag_dictionary
コード例 #20
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
コード例 #21
0
async def character_list(bot, context):
    """Lists the characters of the given user."""
    owner = context.arguments[0] if context.arguments[0] else context.author
    cursor = data.db_select(
        bot, from_arg='characters', where_arg='owner_id=%s', input_args=[owner.id])
    characters = cursor.fetchall() if cursor else []
    if not characters:
        raise CBException("{} has no characters.".format(owner.mention))
    embed = discord.Embed(
        title='Character list',
        description='Owner: {}\n{} character{}'.format(
            owner.mention, len(characters), '' if len(characters) == 1 else 's'))
    for character in characters:
        attributes = character.data['attributes']
        common_attributes = []
        for key in COMMON_ATTRIBUTES:
            if key in attributes:
                common_attributes.append('{}: {}'.format(key, attributes[key]))
        if not common_attributes:
            common_attributes = ['No common attributes']
        embed.add_field(name=character.name, value='\u200b{}'.format('\n'.join(common_attributes)))
    return Response(embed=embed)
コード例 #22
0
ファイル: utilities.py プロジェクト: sasma/JshBot
async def _schedule_timer(bot, raw_entry, delay):
    task_comparison = bot.schedule_timer
    await asyncio.sleep(0.5)
    logger.debug("_schedule_timer sleeping for %s seconds...", delay)
    await asyncio.sleep(delay)
    if task_comparison is not bot.schedule_timer:
        logger.debug(
            "_schedule_timer was not cancelled! Cancelling this scheduler...")
        return
    try:
        cursor = data.db_select(bot,
                                select_arg='min(time)',
                                from_arg='schedule')
        minimum_time = cursor.fetchone()[0]
        data.db_delete(bot,
                       'schedule',
                       where_arg='time=%s',
                       input_args=[minimum_time],
                       safe=False)
    except Exception as e:
        logger.warn("_schedule_timer failed to delete schedule entry. %s", e)
        raise e
    try:
        logger.debug("_schedule_timer done sleeping for %s seconds!", delay)
        scheduled_time, plugin, function, payload, search, destination, info = raw_entry
        if payload:
            payload = json.loads(payload)
        plugin = bot.plugins[plugin]
        function = getattr(plugin, function)
        late = delay < -60
        asyncio.ensure_future(
            function(bot, scheduled_time, payload, search, destination, late))
    except Exception as e:
        logger.warn("Failed to execute scheduled function: %s", e)
        raise e
    asyncio.ensure_future(_start_scheduler(bot))
コード例 #23
0
ファイル: no_awoo.py プロジェクト: jkchen2/JshBot-plugins
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
コード例 #24
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
コード例 #25
0
async def _process_data(bot, author, url, propagate_error=False):
    """Checks the given data and edits/adds an entry to the database."""
    error_code = 1
    raw_data = await utilities.download_url(bot, url, use_fp=True)
    reader = codecs.getreader('utf-8')  # SO: 6862770
    try:
        parsed = json.load(reader(raw_data))
    except Exception as e:
        raise CBException("Failed to load the raw data.", e=e)

    # Check that values are within proper ranges
    try:
        if 'version' not in parsed:
            raise CBException("Missing version number.")
        if 'type' not in parsed:
            raise CBException("Missing character type.")
        if 'name' not in parsed:
            raise CBException("Missing name.")
        if 'attributes' not in parsed:
            raise CBException("Missing attributes.")
        if 'attribute_order' not in parsed:
            raise CBException("Missing attribute order.")
        if 'thumbnail' not in parsed:
            raise CBException("Missing thumbnail.")
        if 'images' not in parsed:
            raise CBException("Missing images.")
        if 'embed_color' not in parsed:
            raise CBException("Missing embed color.")
        if 'tags' not in parsed:
            raise CBException("Missing tags.")

        # Check version
        total_characters = 0
        version = parsed['version']
        if not isinstance(version, int):
            raise CBException("Invalid version type. [int]")
        if version != DATA_VERSION:
            error_code = 4
            raise CBException(
                "Invalid or outdated data format. Please use the character creator site.")

        # Check type and name
        character_type = parsed['type']
        if character_type not in CHARACTER_TYPES:
            raise CBException("Invalid character type.")
        name = parsed['name']
        clean_name = _clean_text_wrapper(name)
        if not isinstance(name, str):
            raise CBException("Invalid name type. [string]")
        if not 1 <= len(name) <= 100:
            raise CBException("Invalid name length. [1-100]")
        total_characters += len(name)

        # Check attributes
        attributes = parsed['attributes']
        if not isinstance(attributes, dict):
            raise CBException("Invalid attributes type. [dictionary]")
        if not 0 <= len(attributes) <= 20:
            raise CBException("Invalid number of attributes. [1-20]")
        for key, value in attributes.items():
            if not isinstance(value, str):
                raise CBException("An attribute has an invalid type. [string]")
            if not 1 <= len(key) <= 50:
                raise CBException("Invalid attribute name length. [1-50]")
            if key in COMMON_ATTRIBUTES and not 1 <= len(value) <= 50:
                raise CBException("Invalid common attribute value length. [1-50]")
            elif not 1 <= len(value) <= 1000:
                raise CBException("Invalid attribute value length. [1-1000]")
            total_characters += len(key) + len(value)

        # Check thumbnail
        thumbnail = parsed['thumbnail']
        if not isinstance(thumbnail, (str, type(None))):
            raise CBException("Invalid thumbnail type. [string]")
        if isinstance(thumbnail, str) and not _valid_url(thumbnail):
            error_code = 2
            raise CBException("Invalid thumbnail URL.")

        # Check images
        images = parsed['images']
        if not isinstance(images, list):
            raise CBException("Invalid images type. [list]")
        if not 0 <= len(images) <= 10:
            raise CBException("Invalid number of images. [0-10]")
        for image in images:
            if not isinstance(image, list):
                raise CBException("Invalid image type. [list]")
            if len(image) != 3:
                raise CBException("Invalid image metadata length. [3]")
            for meta in image:
                if not isinstance(meta, str):
                    raise CBException("Invalid image metadata type. [string]")
            if not 1 <= len(image[0]) <= 500:  # Direct image URL
                raise CBException("Invalid direct image URL length. [1-500]")
            if not _valid_url(image[0]):
                error_code = 3
                raise CBException("Invalid direct image URL.")
            if not 0 <= len(image[1]) <= 500:  # Source URL
                raise CBException("Invalid source URL length. [0-500]")
            if not 0 <= len(image[2]) <= 100:  # Caption
                raise CBException("Invalid image caption length. [0-100]")

        # Check embed color
        embed_color = parsed['embed_color']
        if not isinstance(embed_color, (int, type(None))):
            raise CBException("Invalid embed color type. [int]")
        if isinstance(embed_color, int) and not 0x0 <= embed_color <= 0xffffff:
            raise CBException("Invalid embed color range. [0x0-0xffffff]")

        # Version 2 stuff
        attribute_order = parsed['attribute_order']
        if not isinstance(attribute_order, list):
            raise CBException("Invalid attribute order type. [list]")
        tags = parsed['tags']
        if not isinstance(tags, list):
            raise CBException("Invalid tags type. [list]")

        # Check attribute_order
        order_set = set(attribute_order)
        attribute_set = set(attributes)
        if len(attribute_order) != len(order_set):
            raise CBException("Duplicate attribute order entry.")
        if order_set != attribute_set:
            raise CBException("Attribute order does not match attribute set.")

        # Check tags
        tags = parsed['tags']
        tags_raw = parsed['tags_raw']
        if not 0 <= len('#'.join(tags)) <= 260:  # +60 for name and type
            raise CBException("Invalid tags length. [0-200]")
        if clean_name not in tags:
            raise CBException("Character name not in tags.")
        for character_type in CHARACTER_TYPES:
            if character_type in tags:
                break
        else:
            raise CBException("Character type not in tags.")
        if len(set(tags)) != len(tags):
            raise CBException("Duplicate tags exist.")
        for tag in tags:
            test = _clean_text_wrapper(tag, lowercase=False)
            if test != tag:
                raise CBException("Invalid tag.")
            total_characters += len(tag)

        if total_characters > 3000:
            raise CBException("Total characters exceeded 3000.")

    except BotException as e:
        if propagate_error:
            raise e
        else:
            await author.send("The data checks failed. Error:\n{}".format(e.error_details))
            return error_code

    created_time = int(time.time())
    dt = datetime.datetime.utcfromtimestamp(created_time)

    json_data = Json({
        'type': character_type,
        'version': DATA_VERSION,
        'name': name,
        'clean_name': clean_name,
        'owner_id': author.id,
        'attributes': attributes,
        'attribute_order': attribute_order,
        'thumbnail': thumbnail,
        'images': images,
        'embed_color': embed_color,
        'tags': tags,
        'tags_raw': tags_raw,
        'created': created_time
    })

    # Check for edit or entry creation
    cursor = data.db_select(
        bot, select_arg='clean_name', from_arg='characters', where_arg='owner_id=%s',
        input_args=[author.id])
    existing_names = [it[0] for it in cursor.fetchall()] if cursor else []
    if clean_name in existing_names:  # Edit
        data.db_update(
            bot, 'characters', set_arg='name=%s, data=%s, tags=%s, modified=%s',
            where_arg='owner_id=%s AND clean_name=%s',
            input_args=(name, json_data, tags, dt, author.id, clean_name))
        content = "Edited the entry for {}.".format(name)
    else:  # Create
        data.db_insert(
            bot, 'characters', input_args=[author.id, name, clean_name, json_data, tags, dt])
        content = "Created a new entry for {}.".format(name)

    if propagate_error:
        return content
    else:
        await author.send(content)
        return 0