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
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"))
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")
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)
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()
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), }
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), }
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() }
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)
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))
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
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)
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))
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")
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)
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]}))
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()
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] }))
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)