<commit_before>""" Any utility functions.

This module holds the owner data along with a handful of
command specific functions and helpers.
"""

from enum import Enum
from functools import wraps
import shlex
import re

import discord
import asyncio
import aiohttp

from pcbot import Config, config

owner_cfg = Config("owner")
member_mention_regex = re.compile(r"<@!?(?P<id>\d+)>")
channel_mention_regex = re.compile(r"<#(?P<id>\d+)>")
markdown_code_regex = re.compile(r"^(?P<capt>`*)(?:[a-z]+\n)?(?P<code>.+)(?P=capt)$", flags=re.DOTALL)
identifier_prefix = re.compile(r"[a-zA-Z_]")


class Annotate(Enum):
    """ Command annotation enum.
    Annotate a command argument with one of these to get the commented result. """
    Content = 1  # Return all the content after command and/or arguments with Message.content
    LowerContent = 2  # Same as above but returns the contents in lowercase
    CleanContent = 3  # Return all the content after command and/or arguments with Message.clean_content
    LowerCleanContent = 4  # Same as above but returns the contents in lowercase
    User = Member = 5  # Return a member (uses utils.find_member with steps=3)
    Channel = 6  # Return a channel (uses utils.find_channel with steps=3)
    Self = 7  # Used as a default for Member/Channel annotations and returns the message.author/message.channel
    Code = 8  # Get formatted code (like Content but extracts any code)


def int_range(f: int=None, t: int=None):
    """ Return a helper function for checking if a str converted to int is in the
    specified range, f (from) - t (to).

    If either f or t is None, they will be treated as -inf +inf respectively. """
    def wrapped(arg: str):
        # Convert to int and return None if unsuccessful
        try:
            num = int(arg)
        except ValueError:
            return None

        # Compare the lowest and highest numbers
        if (f and num < f) or (t and num > t):
            return None

        # The string given is converted to a number and fits the criteria
        return num

    return wrapped


def choice(*options, ignore_case: bool=True):
    """ Return a helper function for checking if the argument is either of the given
    options. """
    def wrapped(arg: str):
        # Compare lowercased version
        if ignore_case:
            return arg if arg.lower() in [s.lower() for s in options] else None

        return arg if arg in options else None

    return wrapped


def placeholder(_: str):
    """ Return False. Using this as a command argument annotation will always fail
    the command. Useful for groups. """
    return False


def format_usage(command):
    """ Format the usage string of the given command. Places any usage
    of a sub command on a newline.

    :param command: type plugins.Command """
    if command.hidden and command.parent is not None:
        return

    usage = [command.usage]
    for sub_command in command.sub_commands:
        # Recursively format the usage of the next sub commands
        formatted = format_usage(sub_command)

        if formatted:
            usage.append(formatted)

    return "\n".join(s for s in usage if s is not None) if usage else None


def format_help(command):
    """ Format the help string of the given command as a message to be sent.

    :param command: type plugins.Command """
    usage = format_usage(command)

    # If there is no usage, the command isn't supposed to be displayed as such
    # Therefore, we switch to using the parent command instead
    if usage is None and command.parent is not None:
        return format_help(command.parent)

    desc = command.description

    # Notify the user when a command is owner specific
    if getattr(command.function, "__owner__", False):
        desc += "\n:information_source:`Only the bot owner can execute this command.`"

    # Format aliases
    alias_format = ""
    if command.aliases:
        alias_format = "\n**Aliases**: ```{}```".format(
            ", ".join((config.command_prefix if identifier_prefix.match(alias[0]) else "") + alias
                      for alias in command.aliases))

    return "**Usage**: ```{}```**Description**: {}{}".format(usage, desc, alias_format)


def is_owner(member):
    """ Return true if user/member is the assigned bot owner.

    :param member: discord.User, discord.Member or a str representing the member's ID. """
    if type(member) is not str:
        member = member.id

    if member == owner_cfg.data:
        return True

    return False


def owner(func):
    """ Decorator that runs the command only if the author is the owner. """
    if not asyncio.iscoroutine(func):
        func = asyncio.coroutine(func)
<commit_msg>Do not show prefixes for aliases when they are a subcommand.<commit_after>""" Any utility functions.

This module holds the owner data along with a handful of
command specific functions and helpers.
"""

from enum import Enum
from functools import wraps
import shlex
import re

import discord
import asyncio
import aiohttp

from pcbot import Config, config

owner_cfg = Config("owner")
member_mention_regex = re.compile(r"<@!?(?P<id>\d+)>")
channel_mention_regex = re.compile(r"<#(?P<id>\d+)>")
markdown_code_regex = re.compile(r"^(?P<capt>`*)(?:[a-z]+\n)?(?P<code>.+)(?P=capt)$", flags=re.DOTALL)
identifier_prefix = re.compile(r"[a-zA-Z_]")


class Annotate(Enum):
    """ Command annotation enum.
    Annotate a command argument with one of these to get the commented result. """
    Content = 1  # Return all the content after command and/or arguments with Message.content
    LowerContent = 2  # Same as above but returns the contents in lowercase
    CleanContent = 3  # Return all the content after command and/or arguments with Message.clean_content
    LowerCleanContent = 4  # Same as above but returns the contents in lowercase
    User = Member = 5  # Return a member (uses utils.find_member with steps=3)
    Channel = 6  # Return a channel (uses utils.find_channel with steps=3)
    Self = 7  # Used as a default for Member/Channel annotations and returns the message.author/message.channel
    Code = 8  # Get formatted code (like Content but extracts any code)


def int_range(f: int=None, t: int=None):
    """ Return a helper function for checking if a str converted to int is in the
    specified range, f (from) - t (to).

    If either f or t is None, they will be treated as -inf +inf respectively. """
    def wrapped(arg: str):
        # Convert to int and return None if unsuccessful
        try:
            num = int(arg)
        except ValueError:
            return None

        # Compare the lowest and highest numbers
        if (f and num < f) or (t and num > t):
            return None

        # The string given is converted to a number and fits the criteria
        return num

    return wrapped


def choice(*options, ignore_case: bool=True):
    """ Return a helper function for checking if the argument is either of the given
    options. """
    def wrapped(arg: str):
        # Compare lowercased version
        if ignore_case:
            return arg if arg.lower() in [s.lower() for s in options] else None

        return arg if arg in options else None

    return wrapped


def placeholder(_: str):
    """ Return False. Using this as a command argument annotation will always fail
    the command. Useful for groups. """
    return False


def format_usage(command):
    """ Format the usage string of the given command. Places any usage
    of a sub command on a newline.

    :param command: type plugins.Command """
    if command.hidden and command.parent is not None:
        return

    usage = [command.usage]
    for sub_command in command.sub_commands:
        # Recursively format the usage of the next sub commands
        formatted = format_usage(sub_command)

        if formatted:
            usage.append(formatted)

    return "\n".join(s for s in usage if s is not None) if usage else None


def format_help(command):
    """ Format the help string of the given command as a message to be sent.

    :param command: type plugins.Command """
    usage = format_usage(command)

    # If there is no usage, the command isn't supposed to be displayed as such
    # Therefore, we switch to using the parent command instead
    if usage is None and command.parent is not None:
        return format_help(command.parent)

    desc = command.description

    # Notify the user when a command is owner specific
    if getattr(command.function, "__owner__", False):
        desc += "\n:information_source:`Only the bot owner can execute this command.`"

    # Format aliases
    alias_format = ""
    if command.aliases:
        alias_format = "\n**Aliases**: ```{}```".format(
            ", ".join((config.command_prefix if identifier_prefix.match(alias[0]) and command.parent is None else "") +
                      alias for alias in command.aliases))

    return "**Usage**: ```{}```**Description**: {}{}".format(usage, desc, alias_format)


def is_owner(member):
    """ Return true if user/member is the assigned bot owner.

    :param member: discord.User, discord.Member or a str representing the member's ID. """
    if type(member) is not str:
        member = member.id

    if member == owner_cfg.data:
        return True

    return False


def owner(func):
    """ Decorator that runs the command only if the author is the owner. """
    if not asyncio.iscoroutine(func):
        func = asyncio.coroutine(func)

{'fraction_non_alphanumeric': 0.09140690817186184, 'fraction_numerical': 0.002316764953664701, 'mean_word_length': 2.9753034742570112, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2795223', 'n_tokens_mistral': 2686, 'n_tokens_neox': 2579, 'n_words': 1114}