예제 #1
0
 def add_parser(self, name, **kwargs):
     # set prog from the existing prefix
     if kwargs.get('prog') is None:
         kwargs['prog'] = "%s %s" % (self._prog_prefix, name)
     # create a pseudo-action to hold the choice help
     aliases = kwargs.pop('aliases', [])
     if 'help' in kwargs:
         help = kwargs.pop('help')
         # see [Python2] argparse.py:1029 and [Python3] argparse.py:1059
         args = (name, aliases, help) if PYTHON3 else (name, help)
         choice_action = self._ChoicesPseudoAction(*args)
         self._choices_actions.append(choice_action)
     # create the parser, but with another formatter and separating the help
     #  into an argument group
     parser = self._parser_class(ArgumentParser.globals_dict,
                                 add_help=False,
                                 **kwargs)
     parser.name = name
     i = parser.add_argument_group(gt("extra arguments"))
     i.add_argument("-h",
                    "--help",
                    action='help',
                    default=SUPPRESS,
                    help=gt("show this help message and exit"))
     # add it to the map
     self._name_parser_map[name] = parser
     # make parser available under aliases also (Python3 only ; see before)
     for alias in aliases:
         self._name_parser_map[alias] = parser
     return parser
예제 #2
0
 def _input_arg(self, a):
     """
     Ask the user for input of a single argument.
     
     :param a: argparse.Action instance
     :return:  the user input, asked according to the action
     """
     # if action of an argument that suppresses any other, just return
     if a.dest == SUPPRESS or a.default == SUPPRESS:
         return
     # prepare the prompt
     prompt = gt((a.help or a.dest).capitalize())
     r = {'newline': True, 'required': a.required}
     # now handle each different action
     if self.is_action(a, 'store', 'append'):
         return user_input(prompt, a.choices, a.default, **r)
     elif self.is_action(a, 'store_const', 'append_const'):
         return user_input(prompt, (gt("(A)dd"), gt("(D)iscard")), "d", **r)
     elif self.is_action(a, 'store_true'):
         return user_input(prompt, (gt("(Y)es"), gt("(N)o")), "n", **r)
     elif self.is_action(a, 'store_false'):
         return user_input(prompt, (gt("(Y)es"), gt("(N)o")), "y", **r)
     elif self.is_action(a, 'count'):
         return user_input(prompt, is_pos_int, 0, gt("positive integer"),
                           **r)
     elif self.is_action(a, 'parsers'):
         pmap = a._name_parser_map
         _ = list(pmap.keys())
         return user_input(prompt, _, _[0], **r) if len(_) > 0 else None
     raise NotImplementedError(gt("Unknown argparse action"))
예제 #3
0
 def __init__(self, option_strings, dest=None, default=None, help=None):
     super(_ConfigAction, self).__init__(option_strings=option_strings,
                                         dest=SUPPRESS,
                                         default=default,
                                         nargs=1,
                                         help=gt(help),
                                         metavar="INI")
예제 #4
0
def create_zone(request):
    template_zone = request.GET.get('template_zone', '').strip('"')

    context = None
    message = ''
    zones = get_zones()
    if template_zone:
        try:
            root_domain = zones.get(name=template_zone)
            context = {
                'message': 'Using {0} as a template.'.format(template_zone),
                'root_domain': root_domain.name,
                'contact': root_domain.soa.contact,
                'primary': root_domain.soa.primary,
                'nss': root_domain.nameserver_set.all(),
                'zones': json.dumps(
                    sorted([z.name for z in get_zones()], reverse=True))
            }
        except ObjectDoesNotExist:
            message = gt('When trying to use {0} as a template, no zone '
                         'named {0} was found.'.format(template_zone))
    if not context:
        context = {
            'message': message,
            'root_domain': '',
            'contact': '',
            'primary': '',
            'nss': [],
            'zones': json.dumps(
                sorted([z.name for z in get_zones()], reverse=True))
        }

    return render(request, 'create_zone/create_zone.html', context)
예제 #5
0
 def __call__(self, parser, namespace, values, option_string=None):
     conf = values[0]
     setattr(namespace, "read_config", conf)
     if conf not in parser._config.read(conf):
         logger.error(gt("Config file '{}' not found").format(conf))
         sys.exit(2)
     parser.config_args()
예제 #6
0
 def post_data(self):
     return {
         # We are f*****g this up on purpose.
         'fuckinup': random_label(),
         'fqdn': gt(random_label() + '.' + random_label() + '.' +
                    self.domain.name),
     }
예제 #7
0
 def post_data(self):
     return {
         # We are f*****g this up on purpose.
         'fuckinup':
         random_label(),
         'fqdn':
         gt('c' + random_label() + '.' + random_label() + '.' +
            self.domain.name),
     }
예제 #8
0
 def post_data(self):
     return {
         'description': random_label(),
         'ttl': random_byte(),
         'ip_str': gt("11.{0}.{1}.{2}".format(random_byte(), random_byte(),
                      random_byte())),
         'ip_type': '4',
         'name': random_label()
     }
예제 #9
0
 def add_argument(self, *args, **kwargs):
     new_kw = {k: v for k, v in kwargs.items()}
     # collect Tinyscript-added keyword-arguments
     cancel = new_kw.pop('cancel', False)
     orig = new_kw.pop('orig', None)
     note = new_kw.pop('note', None)
     last = new_kw.pop('last', False)
     prefix = new_kw.pop('prefix', None)
     suffix = new_kw.pop('suffix', None)
     try:
         # define the action based on argparse, with only argparse-known
         #  keyword-arguments
         action = super(_NewActionsContainer,
                        self).add_argument(*args, **new_kw)
         # now set Tinyscript-added keyword-arguments
         action.note = None if note is None else gt(note)
         action.last = last
         action.orig = orig
         action.prefix = prefix
         action.suffix = suffix
         return args[-1]
     except ArgumentError:
         # drop the argument if conflict and cancel set to True
         if cancel:
             return
         # otherwise, retry after removing the short option string
         args = list(args)
         short_opt = list(filter(is_short_opt, args))
         if len(short_opt) > 0:
             args.remove(short_opt[0])
             return self.add_argument(*args, **kwargs)
         # otherwise, retry after modifying the long option string with the
         #  precedence to the prefix (if set) then the suffix (if set)
         long_opt = list(filter(is_long_opt, args))
         if len(long_opt) > 0:
             long_opt = args.pop(args.index(long_opt[0]))
             if kwargs.get('action') in \
                 [None, 'store', 'append', 'store_const', 'append_const']:
                 # set metavar only if no choices given ; otherwise, it
                 #  takes the precedence on choices in the help message
                 kwargs['metavar'] = kwargs.get('metavar') or \
                                     (long_opt.lstrip('-').upper() \
                                      if not kwargs.get('choices') else None)
             curr_opt = long_opt.lstrip("-")
             kwargs['orig'] = curr_opt.replace("-", "_")
             if prefix:
                 long_opt = "--{}-{}".format(prefix, curr_opt)
                 args.append(long_opt)
                 return self.add_argument(*args, **kwargs)
             elif suffix:
                 long_opt = "{}-{}".format(long_opt, suffix)
                 args.append(long_opt)
                 return self.add_argument(*args, **kwargs)
예제 #10
0
 def error(self, message):
     """
     Prints a usage message incorporating the message to stderr and exits in
      the case when no new arguments to be reparsed, that is when no special
      action like _DemoAction (triggering parser.demo_args()) or 
      _WizardAction (triggering input_args()) was called. Otherwise, it
      simply does not stop execution so that new arguments can be reparsed.
     """
     if all(len(x) == 0 for x in self._reparse_args.values()):
         # normal behavior with argparse
         self.print_usage(sys.stderr)
         self.exit(2, gt("%s: error: %s\n") % (self.prog, message))
예제 #11
0
 def post_data(self):
     return {
         'description':
         random_label(),
         'ttl':
         random_byte(),
         'ip_str':
         gt("11.{0}.{1}.{2}".format(random_byte(), random_byte(),
                                    random_byte())),
         'ip_type':
         '4',
         'name':
         random_label()
     }
예제 #12
0
    def _(s):
        #todo: permanent variable
        i_strs_test = {}
        for i in range(0, 4):
            i_strs_test[str(i)] = str(i)

        i = s
        try:
            #test translating the string with many replacements
            i = gt(s)
            test = i % i_strs_test
        except:
            #if it doesn't work, revert
            i = s

        return i
예제 #13
0
 def __init__(self, globals_dict=None, *args, **kwargs):
     self._config_parsed = False
     self._reparse_args = {'pos': [], 'opt': [], 'sub': []}
     ArgumentParser.globals_dict = gd = globals_dict or {}
     self.examples = gd.get('__examples__')
     if self.examples == [] and len(self.examples) == 0:
         self.examples = None
     script = gd.get('__file__') or sys.argv[0]
     if script and kwargs.get('prog') is None:
         path = abspath(script)
         root = dirname(path)
         script = basename(script)
         kwargs['prog'] = "python{} ".format(["", "3"][PYTHON3]) + \
                        script if not access(path, X_OK) else "./" + script \
                        if root not in environ['PATH'].split(":") else script
         ArgumentParser.prog = kwargs['prog']
         script, _ = splitext(script)
     kwargs['add_help'] = False
     kwargs['conflict_handler'] = "error"
     kwargs['formatter_class'] = HelpFormatter
     # format the epilog message
     if self.examples and script:
         _ = ["{} {}".format(ArgumentParser.prog, e) for e in self.examples]
         _ = list(filter(lambda x: x.startswith(kwargs['prog']), _))
         if len(_) > 0:
             kwargs['epilog'] = gt("Usage example{}"
                                   .format(["", "s"][len(_) > 1])) + \
                                ":\n" + '\n'.join("  " + e for e in _)
     # adapt the script name ; if SCRIPTNAME is provided, it supersedes
     #  SCRIPTNAME_FORMAT, otherwise compute the name according to the
     #  format specified in SCRIPTNAME_FORMAT
     sname = gd.get('__script__')
     if sname is None:
         sname_fmt = gd.get('SCRIPTNAME_FORMAT', SCRIPTNAME_FORMAT)
         sname_func = SCRIPTNAME_FORMATS.get(sname_fmt)
         if sname_func:
             sname = sname_func(script)
         else:
             l = "\n- ".join(sorted(SCRIPTNAME_FORMATS.keys()))
             raise ValueError("Bad script name format ; please use one of "
                              "the followings:\n{}".format(l))
     self.scriptname = sname
     # format the description message
     d = sname
     v = str(gd.get('__version__') or "")
     if v:
         d += " v" + v
     v = gd.get('__status__')
     if v:
         d += " (" + v + ")"
     l = max(list(map(lambda x: len(x.strip('_')), BASE_DUNDERS)))
     for k in BASE_DUNDERS:
         m = gd.get(k)
         if m:
             if k == '__copyright__':
                 m = copyright(m)
             elif k == '__license__':
                 m = license(m, True) or m
             d += ("\n{: <%d}: {}" % l).format(k.strip('_').capitalize(), m)
             if k == '__author__':
                 e = gd.get('__email__')
                 if e:
                     d += " ({})".format(e)
     doc = gd.get('__doc__')
     if doc:
         d += "\n\n" + doc
     kwargs['description'] = d
     self.details = gd.get('__details__', [])
     # now initialize argparse's ArgumentParser with the new arguments
     super(ArgumentParser, self).__init__(*args, **kwargs)
예제 #14
0
 def __init__(self, option_strings, dest=SUPPRESS, help=None):
     super(_DemoAction, self).__init__(option_strings=option_strings,
                                       dest=SUPPRESS,
                                       default=SUPPRESS,
                                       nargs=0,
                                       help=gt(help))
예제 #15
0
 def _set_arg(self, a, s="main", c=False):
     """
     Set a single argument.
     
     :param a: argparse.Action instance
     :param s: config section title
     :param c: use class' ConfigParser instance to get parameters
     """
     # if action of an argument that suppresses any other, just return
     if a.dest is SUPPRESS or a.default is SUPPRESS:
         return
     # check if an option string is used for this action in sys.argv ;
     #  if so, simply return as it will be parsed normally
     if any(o in sys.argv[1:] for o in a.option_strings):
         return
     # in case of non-null config, get the value from the config object
     default = a.default if a.default is None else str(a.default)
     if c:
         try:
             value = ArgumentParser._config.get(s, a.dest)
         except (NoOptionError, NoSectionError) as e:
             item = "setting" if isinstance(e, NoOptionError) else "section"
             # if the argument is required, just ask for the value
             value = self._input_arg(a) if a.required else default
             logger.debug(
                 gt("{} {} not present in config (set to {})").format(
                     a.dest, item, value))
     # in case of null config, just ask for the value
     else:
         value = self._input_arg(a)
     # collect the option string before continuing
     try:
         ostr = a.option_strings[0]
     except IndexError:  # occurs when positional argument
         ostr = None
     # now handle arguments regarding the action
     if self.is_action(a, 'store', 'append'):
         if value:
             if ostr:
                 self._reparse_args['opt'].extend([ostr, value])
             else:
                 self._reparse_args['pos'].extend([value])
     elif self.is_action(a, 'store_const', 'append_const'):
         if value.lower() == "add" or value != default:
             self._reparse_args['opt'].append(ostr)
     elif self.is_action(a, 'store_true'):
         if value.lower() in ["y", "true"]:
             self._reparse_args['opt'].append(ostr)
     elif self.is_action(a, 'store_false'):
         if value.lower() in ["n", "false"]:
             self._reparse_args['opt'].append(ostr)
     elif self.is_action(a, 'count'):
         v = int(value or 0)
         if v > 0:
             if ostr.startswith("--"):
                 new_arg = [ostr for i in range(v)]
             else:
                 new_arg = ["-{}".format(v * ostr.strip('-'))]
             self._reparse_args['opt'].extend(new_arg)
     elif self.is_action(a, 'parsers'):
         if not value:
             value = self._input_arg(a)
         pmap = a._name_parser_map
         if c:
             pmap[value].config_args(a.dest)
             pmap[value]._reparse_args['pos'].insert(0, value)
         else:
             pmap[value].input_args()
         self._reparse_args['sub'].append(pmap[value])
     else:
         raise NotImplementedError("Unknown argparse action")
예제 #16
0
파일: utils.py 프로젝트: alecdhuse/cyder
    public_view, _ = View.objects.get_or_create(name='public')
    saved_nss = []  # If these are errors, back out
    for i, ns in enumerate(nss):
        ns.domain = domain
        try:
            ns.save()
            ns.views.add(private_view)
            if not domain.name.endswith('10.in-addr.arpa'):
                ns.views.add(public_view)
            saved_nss.append(ns)
        except ValidationError, e:
            suffixes = ["th", "st", "nd", "rd", ] + ["th"] * 16
            suffixed_i = str(i + 1) + suffixes[i + 1 % 100]
            error_field, error_val = e.message_dict.items()[0]
            error = gt("When trying to create the {0} nameserver entry, the "
                       "nameserver field '{1}' returned the error "
                       "'{2}'".format(suffixed_i, error_field, error_val[0]))

            for s_ns in saved_nss:
                s_ns.delete()
            _clean_domain_tree(domain)
            soa.delete()
            return HttpResponse(json.dumps({'success': False,
                                            'error': error}))
    try:
        domain.soa = soa
        domain.save()
    except ValidationError, e:
        for s_ns in saved_nss:
            s_ns.delete()
        _clean_domain_tree(domain)
예제 #17
0
파일: helpers.py 프로젝트: alecdhuse/cyder
def create_zone_ajax(request):
    """This view tries to create a new zone and returns an JSON with either
    'success' = True or 'success' = False and some errors. By default all
    records are created and added to the public view.

    Throughout this function note that objects that are created are recorded,
    and if an error is caught, the previously created objects are deleted. This
    backing-out *may* be better handling by a transaction. Django has this sort
    of thing (middleware and decorators), but I'm in a time crunch so this
    manual deletetion will have to do.
    """

    qd = request.POST.copy()
    # See if the domain exists.
    # Fail if it already exists or if it's under a delegated domain.
    root_domain = qd.get('root_domain', None)

    if not root_domain:
        error = "Please specify a root_domain"
        return HttpResponse(json.dumps({'success': False, 'error': error}))

    if Domain.objects.filter(name=root_domain).exists():
        error = gt("<b>{0}</b> is already a domain. To make it a new zone, "
                   "assign it a newly created SOA.".format(root_domain))
        return HttpResponse(json.dumps({'success': False, 'error': error}))

    primary = qd.get('soa_primary', None)
    if not primary:
        error = "Please specify a primary nameserver for the SOA record."
        return HttpResponse(json.dumps({'success': False, 'error': error}))
    contact = qd.get('soa_contact', None)
    if not contact:
        error = "Please specify a contact address for the SOA record."
        return HttpResponse(json.dumps({'success': False, 'error': error}))
    contact.replace('@', '.')

    # Find all the NS entries
    nss = []
    for k, v in request.POST.iteritems():
        if k.startswith('nameserver_'):
            ttl = qd.get('ttl_{0}'.format(k[-1:]), 3600)
            server = v
            nss.append(Nameserver(server=server, ttl=ttl))

    if not nss:
        # They must create at least one nameserver
        error = gt("You must choose an authoritative nameserver to serve this "
                   "zone")
        return HttpResponse(json.dumps({'success': False, 'error': error}))

    # We want all domains created up to this point to inherit their
    # master_domain's soa. We will override the return domain's SOA.
    # Everything under this domain can be purgeable becase we will set this
    # domain to non-purgeable. This will also allow us to call prune tree.
    domain = ensure_domain(root_domain, purgeable=True,
                           inherit_soa=False, force=True)

    soa = SOA(primary=primary, contact=contact, serial=int(time.time()),
              description="SOA for {0}".format(root_domain))
    try:
        soa.save()
    except ValidationError, e:
        _clean_domain_tree(domain)
        return HttpResponse(json.dumps({'success': False,
                                        'error': e.messages[0]}))
예제 #18
0
            ns.save()
            ns.views.add(private_view)
            if not domain.name.endswith('10.in-addr.arpa'):
                ns.views.add(public_view)
            saved_nss.append(ns)
        except ValidationError, e:
            suffixes = [
                "th",
                "st",
                "nd",
                "rd",
            ] + ["th"] * 16
            suffixed_i = str(i + 1) + suffixes[i + 1 % 100]
            error_field, error_val = e.message_dict.items()[0]
            error = gt("When trying to create the {0} nameserver entry, the "
                       "nameserver field '{1}' returned the error "
                       "'{2}'".format(suffixed_i, error_field, error_val[0]))

            for s_ns in saved_nss:
                s_ns.delete()
            _clean_domain_tree(domain)
            soa.delete()
            return HttpResponse(json.dumps({'success': False, 'error': error}))
    try:
        domain.soa = soa
        domain.save()
    except ValidationError, e:
        for s_ns in saved_nss:
            s_ns.delete()
        _clean_domain_tree(domain)
        soa.delete()
예제 #19
0
def create_zone_ajax(request):
    """
    This view tries to create a new zone and returns an JSON with either
    'success' = True or 'success' = False and some errors. By default all
    records are created and added to the public view.

    Throughout this function note that objects that are created are recorded,
    and if an error is caught, the previously created objects are deleted. This
    backing-out *may* be better handling by a transaction. Django has this sort
    of thing (middleware and decorators), but I'm in a time crunch so this
    manual deletetion will have to do.
    """

    qd = request.POST.copy()
    # See if the domain exists.
    # Fail if it already exists or if it's under a delegated domain.
    root_domain = qd.get('root_domain', None)

    if not root_domain:
        error = "Please specify a root_domain"
        return HttpResponse(json.dumps({'success': False, 'error': error}))

    if Domain.objects.filter(name=root_domain).exists():
        error = gt("<b>{0}</b> is already a domain. To make it a new zone, "
                   "assign it a newly created SOA.".format(root_domain))
        return HttpResponse(json.dumps({'success': False, 'error': error}))

    primary = qd.get('soa_primary', None)
    if not primary:
        error = "Please specify a primary nameserver for the SOA record."
        return HttpResponse(json.dumps({'success': False, 'error': error}))
    contact = qd.get('soa_contact', None)
    if not contact:
        error = "Please specify a contact address for the SOA record."
        return HttpResponse(json.dumps({'success': False, 'error': error}))
    contact.replace('@', '.')

    # Find all the NS entries
    nss = []
    for k, v in request.POST.iteritems():
        if k.startswith('nameserver_'):
            ttl = qd.get('ttl_{0}'.format(k[-1:]), 3600)
            server = v
            nss.append(Nameserver(server=server, ttl=ttl))

    if not nss:
        # They must create at least one nameserver
        error = gt("You must choose an authoritative nameserver to serve this "
                   "zone")
        return HttpResponse(json.dumps({'success': False, 'error': error}))

    # We want all domains created up to this point to inherit their
    # master_domain's soa. We will override the return domain's SOA.
    # Everything under this domain can be purgeable becase we will set this
    # domain to non-purgeable. This will also allow us to call prune tree.
    domain = ensure_domain(root_domain,
                           purgeable=True,
                           inherit_soa=False,
                           force=True)

    soa = SOA(primary=primary,
              contact=contact,
              serial=int(time.time()),
              description="SOA for {0}".format(root_domain))
    try:
        soa.save()
    except ValidationError, e:
        _clean_domain_tree(domain)
        return HttpResponse(
            json.dumps({
                'success': False,
                'error': e.messages[0]
            }))
예제 #20
0
class ErrorHandler(vbu.Cog):

    COMMAND_ERROR_RESPONSES = (
        (vbu.errors.MissingRequiredArgumentString, lambda ctx, error: gt(
            "errors",
            localedir=LOCALE_PATH,
            languages=[ctx.locale],
            fallback=True
        ).gettext(
            "You're missing `{parameter_name}`, which is required for this command.",
        ).format(parameter_name=error.param)),
        (commands.MissingRequiredArgument,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext(
             "You're missing `{parameter_name}`, which is required for this command.",
         ).format(parameter_name=error.param.name)),
        ((commands.UnexpectedQuoteError,
          commands.InvalidEndOfQuotedStringError,
          commands.ExpectedClosingQuoteError),
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("The quotes in your message have been done incorrectly.", )),
        (commands.CommandOnCooldown,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("You can use this command again in {timestamp}.", ).
         format(timestamp=utils.format_dt(
             utils.utcnow() + timedelta(seconds=error.retry_after), style="R"))
         ),
        (vbu.errors.BotNotReady, lambda ctx, error: gt("errors",
                                                       localedir=LOCALE_PATH,
                                                       languages=[ctx.locale],
                                                       fallback=True).
         gettext(
             "The bot isn't ready to start processing that command yet - please wait.",
         )),
        (commands.NSFWChannelRequired,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("You can only run this command in channels set as NSFW.", )),
        (commands.IsNotSlashCommand,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("This command can only be run as a slash command.", )),
        (commands.DisabledCommand, lambda ctx, error: gt(
            "errors",
            localedir=LOCALE_PATH,
            languages=[ctx.locale],
            fallback=True).gettext("This command has been disabled.", )),
        (vbu.errors.NotBotSupport,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext(
             "You need to be part of the bot's support team to be able to run this command.",
         )),
        (commands.MissingAnyRole, lambda ctx, error: gt("errors",
                                                        localedir=LOCALE_PATH,
                                                        languages=[ctx.locale],
                                                        fallback=True).
         gettext(
             "You need to have at least one of {roles} to be able to run this command.",
         ).format(roles=', '.join(f"`{i.mention}`"
                                  for i in error.missing_roles))),
        (commands.BotMissingAnyRole,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext(
             "I need to have one of the {roles} roles for you to be able to run this command.",
         ).format(roles=', '.join(f"`{i.mention}`"
                                  for i in error.missing_roles))),
        (commands.MissingRole, lambda ctx, error: gt("errors",
                                                     localedir=LOCALE_PATH,
                                                     languages=[ctx.locale],
                                                     fallback=True).
         gettext(
             "You need to have the `{role}` role to be able to run this command.",
         ).format(role=error.missing_role)),
        (commands.BotMissingRole, lambda ctx, error: gt("errors",
                                                        localedir=LOCALE_PATH,
                                                        languages=[ctx.locale],
                                                        fallback=True).
         gettext(
             "I need to have the `{role}` role for you to be able to run this command.",
         ).format(role=error.missing_role)),
        (commands.MissingPermissions,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("You need the `{permission}` permission to run this command.",
                 ).format(permission=error.missing_permissions[0].replace(
                     "_", " "))),
        (commands.BotMissingPermissions,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext(
             "I need the `{permission}` permission for me to be able to run this command.",
         ).format(permission=error.missing_permissions[0].replace("_", " "))),
        (commands.NoPrivateMessage, lambda ctx, error: gt(
            "errors",
            localedir=LOCALE_PATH,
            languages=[ctx.locale],
            fallback=True).gettext("This command can't be run in DMs.", )),
        (commands.PrivateMessageOnly, lambda ctx, error: gt(
            "errors",
            localedir=LOCALE_PATH,
            languages=[ctx.locale],
            fallback=True).gettext("This command can only be run in DMs.", )),
        (commands.NotOwner, lambda ctx, error: gt("errors",
                                                  localedir=LOCALE_PATH,
                                                  languages=[ctx.locale],
                                                  fallback=True).
         gettext("You need to be registered as an owner to run this command.",
                 )),
        (commands.MessageNotFound,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert `{argument}` into a message.", ).format(
             argument=error.argument)),
        (commands.MemberNotFound, lambda ctx, error: gt("errors",
                                                        localedir=LOCALE_PATH,
                                                        languages=[ctx.locale],
                                                        fallback=True).
         gettext("I couldn't convert `{argument}` into a guild member.",
                 ).format(argument=error.argument)),
        (commands.UserNotFound, lambda ctx, error: gt("errors",
                                                      localedir=LOCALE_PATH,
                                                      languages=[ctx.locale],
                                                      fallback=True).
         gettext("I couldn't convert `{argument}` into a user.", ).format(
             argument=error.argument)),
        (commands.ChannelNotFound,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert `{argument}` into a channel.", ).format(
             argument=error.argument)),
        (commands.ChannelNotReadable, lambda ctx, error: gt(
            "errors",
            localedir=LOCALE_PATH,
            languages=[ctx.locale],
            fallback=True).gettext("I can't read messages in <#{id}>.", ).
         format(id=error.argument.id)),
        (commands.BadColourArgument,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert `{argument}` into a colour.", ).format(
             argument=error.argument)),
        (commands.RoleNotFound, lambda ctx, error: gt("errors",
                                                      localedir=LOCALE_PATH,
                                                      languages=[ctx.locale],
                                                      fallback=True).
         gettext("I couldn't convert `{argument}` into a role.", ).format(
             argument=error.argument)),
        (commands.BadInviteArgument,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert `{argument}` into an invite.", ).format(
             argument=error.argument)),
        ((commands.EmojiNotFound, commands.PartialEmojiConversionFailure),
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert `{argument}` into an emoji.", ).format(
             argument=error.argument)),
        (commands.BadBoolArgument,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert `{argument}` into a boolean.", ).format(
             argument=error.argument)),
        (commands.BadUnionArgument,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("I couldn't convert your provided `{parameter_name}`.",
                 ).format(parameter_name=error.param.name)),
        (commands.BadArgument,
         lambda ctx, error: str(error).format(ctx=ctx, error=error)),
        # (
        #     commands.CommandNotFound,  # This is only handled in slash commands
        #     lambda ctx, error: gt("errors", localedir=LOCALE_PATH, languages=[ctx.locale], fallback=True).gettext(
        #         "I wasn't able to find that command to be able to run it.",
        #     )
        # ),
        (commands.MaxConcurrencyReached, lambda ctx, error: gt(
            "errors",
            localedir=LOCALE_PATH,
            languages=[ctx.locale],
            fallback=True).gettext("You can't run this command right now.", )),
        (commands.TooManyArguments,
         lambda ctx, error: gt("errors",
                               localedir=LOCALE_PATH,
                               languages=[ctx.locale],
                               fallback=True).
         gettext("You gave too many arguments to this command.", )),
        (discord.NotFound,
         lambda ctx, error: str(error).format(ctx=ctx, error=error)),
        (commands.CheckFailure,
         lambda ctx, error: str(error).format(ctx=ctx, error=error)),
        (discord.Forbidden, lambda ctx, error: gt("errors",
                                                  localedir=LOCALE_PATH,
                                                  languages=[ctx.locale],
                                                  fallback=True).
         gettext("Discord is saying I'm unable to perform that action.", )),
        ((discord.HTTPException,
          aiohttp.ClientOSError), lambda ctx, error: gt("errors",
                                                        localedir=LOCALE_PATH,
                                                        languages=[ctx.locale],
                                                        fallback=True).
         gettext(
             "Either I or Discord messed up running this command. Please try again later.",
         )),

        # Disabled because they're base classes for the subclasses above
        # (commands.CommandError, lambda ctx, error: ""),
        # (commands.CheckFailure, lambda ctx, error: ""),
        # (commands.CheckAnyFailure, lambda ctx, error: ""),
        # (commands.CommandInvokeError, lambda ctx, error: ""),
        # (commands.UserInputError, lambda ctx, error: ""),
        # (commands.ConversionError, lambda ctx, error: ""),
        # (commands.ArgumentParsingError, lambda ctx, error: ""),

        # Disabled because they all refer to extension and command loading
        # (commands.ExtensionError, lambda ctx, error: ""),
        # (commands.ExtensionAlreadyLoaded, lambda ctx, error: ""),
        # (commands.ExtensionNotLoaded, lambda ctx, error: ""),
        # (commands.NoEntryPointError, lambda ctx, error: ""),
        # (commands.ExtensionFailed, lambda ctx, error: ""),
        # (commands.ExtensionNotFound, lambda ctx, error: ""),
        # (commands.CommandRegistrationError, lambda ctx, error: ""),
    )

    async def send_to_ctx_or_author(
            self,
            ctx: vbu.Context,
            text: str,
            author_text: str = None) -> typing.Optional[discord.Message]:
        """
        Tries to send the given text to ctx, but failing that, tries to send it to the author
        instead. If it fails that too, it just stays silent.
        """

        kwargs = {
            "content": text,
            "allowed_mentions": discord.AllowedMentions.none()
        }
        if isinstance(ctx, commands.SlashContext) and self.bot.config.get(
                "ephemeral_error_messages", True):
            kwargs.update({"ephemeral": True})
        try:
            return await ctx.send(**kwargs)
        except discord.Forbidden:
            kwargs["content"] = text or author_text
            try:
                return await ctx.author.send(**kwargs)
            except discord.Forbidden:
                pass
        except discord.NotFound:
            pass
        return None

    @vbu.Cog.listener()
    async def on_command_error(self, ctx: vbu.Context,
                               error: commands.CommandError):
        """
        Global error handler for all the commands around wew.
        """

        # Set up some errors that are just straight up ignored
        ignored_errors = (
            commands.CommandNotFound,
            vbu.errors.InvokedMetaCommand,
        )
        if isinstance(error, ignored_errors):
            return

        # See what we've got to deal with
        setattr(ctx, "original_author_id",
                getattr(ctx, "original_author_id", ctx.author.id))

        # Set up some errors that the owners are able to bypass
        owner_reinvoke_errors = (
            commands.MissingRole,
            commands.MissingAnyRole,
            commands.MissingPermissions,
            commands.CommandOnCooldown,
            commands.DisabledCommand,
            commands.CheckFailure,
            vbu.errors.IsNotUpgradeChatSubscriber,
            vbu.errors.IsNotVoter,
            vbu.errors.NotBotSupport,
        )
        if isinstance(error, owner_reinvoke_errors
                      ) and ctx.original_author_id in self.bot.owner_ids:
            if not self.bot.config.get("owners_ignore_check_failures",
                                       True) and isinstance(
                                           error, commands.CheckFailure):
                pass
            else:
                return await ctx.reinvoke()

        # See if the command itself has an error handler AND it isn't a locally handlled arg
        # if hasattr(ctx.command, "on_error") and not isinstance(ctx.command, vbu.Command):
        if hasattr(ctx.command, "on_error"):
            return

        # See if it's in our list of common outputs
        output = None
        error_found = False
        for error_types, function in self.COMMAND_ERROR_RESPONSES:
            if isinstance(error, error_types):
                error_found = True
                output = function(ctx, error)
                break

        # See if they're tryina f**k me up
        if output is not None and ctx.message and output in ctx.message.content and isinstance(
                error, commands.NotOwner):
            output = "\N{UNAMUSED FACE}"

        # Send a message based on the output
        if output:
            try:
                _, _ = output
            except ValueError:
                output = (output, )
            return await self.send_to_ctx_or_author(ctx, *output)

        # Make sure not to send an error if it's "handled"
        if error_found:
            return

        # The output isn't a common output -- send them a plain error response
        try:
            await ctx.send(f"`{str(error).strip()}`",
                           allowed_mentions=discord.AllowedMentions.none())
        except (discord.Forbidden, discord.NotFound):
            pass

        # Ping unhandled errors to the owners and to the event webhook
        error_string = "".join(
            traceback.format_exception(None, error, error.__traceback__))
        file_handle = io.StringIO(error_string + "\n")
        guild_id = ctx.guild.id if ctx.guild else None
        error_text = (
            f"Error `{error}` encountered.\nGuild `{guild_id}`, channel `{ctx.channel.id}`, "
            f"user `{ctx.author.id}`\n```\n{ctx.message.content if ctx.message else '[No message content]'}\n```"
        )

        # DM to owners
        if self.bot.config.get('dm_uncaught_errors', False):
            for owner_id in self.bot.owner_ids:
                owner = self.bot.get_user(
                    owner_id) or await self.bot.fetch_user(owner_id)
                file_handle.seek(0)
                await owner.send(error_text,
                                 file=discord.File(file_handle,
                                                   filename="error_log.py"))

        # Ping to the webook
        event_webhook: typing.Optional[
            discord.Webhook] = self.bot.get_event_webhook("unhandled_error")
        try:
            avatar_url = str(self.bot.user.display_avatar.url)
        except Exception:
            avatar_url = None
        if event_webhook:
            file_handle.seek(0)
            try:
                file = discord.File(file_handle, filename="error_log.py")
                await event_webhook.send(
                    error_text,
                    file=file,
                    username=f"{self.bot.user.name} - Error",
                    avatar_url=avatar_url,
                    allowed_mentions=discord.AllowedMentions.none(),
                )
            except discord.HTTPException as e:
                self.logger.error(
                    f"Failed to send webhook for event unhandled_error - {e}")

        # And throw it into the console
        logger = getattr(getattr(ctx, 'cog', self), 'logger', self.logger)
        for line in error_string.strip().split("\n"):
            logger.error(line)