mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-12-06 17:32:31 -05:00
Privatize APIs by renaming or removing them from __all__ (#6021)
This commit is contained in:
326
redbot/core/_cli.py
Normal file
326
redbot/core/_cli.py
Normal file
@@ -0,0 +1,326 @@
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
from enum import IntEnum
|
||||
from typing import Optional
|
||||
|
||||
import discord
|
||||
from discord import __version__ as discord_version
|
||||
|
||||
from redbot.core.utils._internal_utils import cli_level_to_log_level
|
||||
|
||||
|
||||
# This needs to be an int enum to be used
|
||||
# with sys.exit
|
||||
class ExitCodes(IntEnum):
|
||||
#: Clean shutdown (through signals, keyboard interrupt, [p]shutdown, etc.).
|
||||
SHUTDOWN = 0
|
||||
#: An unrecoverable error occurred during application's runtime.
|
||||
CRITICAL = 1
|
||||
#: The CLI command was used incorrectly, such as when the wrong number of arguments are given.
|
||||
INVALID_CLI_USAGE = 2
|
||||
#: Restart was requested by the bot owner (probably through [p]restart command).
|
||||
RESTART = 26
|
||||
#: Some kind of configuration error occurred.
|
||||
CONFIGURATION_ERROR = 78 # Exit code borrowed from os.EX_CONFIG.
|
||||
|
||||
|
||||
def confirm(text: str, default: Optional[bool] = None) -> bool:
|
||||
if default is None:
|
||||
options = "y/n"
|
||||
elif default is True:
|
||||
options = "Y/n"
|
||||
elif default is False:
|
||||
options = "y/N"
|
||||
else:
|
||||
raise TypeError(f"expected bool, not {type(default)}")
|
||||
|
||||
while True:
|
||||
try:
|
||||
value = input(f"{text}: [{options}] ").lower().strip()
|
||||
except KeyboardInterrupt:
|
||||
print("\nAborted!")
|
||||
sys.exit(ExitCodes.SHUTDOWN)
|
||||
except EOFError:
|
||||
print("\nAborted!")
|
||||
sys.exit(ExitCodes.INVALID_CLI_USAGE)
|
||||
if value in ("y", "yes"):
|
||||
return True
|
||||
if value in ("n", "no"):
|
||||
return False
|
||||
if value == "":
|
||||
if default is not None:
|
||||
return default
|
||||
print("Error: invalid input")
|
||||
|
||||
|
||||
async def interactive_config(red, token_set, prefix_set, *, print_header=True):
|
||||
token = None
|
||||
|
||||
if print_header:
|
||||
print("Red - Discord Bot | Configuration process\n")
|
||||
|
||||
if not token_set:
|
||||
print(
|
||||
"Please enter a valid token.\n"
|
||||
"You can find out how to obtain a token with this guide:\n"
|
||||
"https://docs.discord.red/en/stable/bot_application_guide.html#creating-a-bot-account"
|
||||
)
|
||||
while not token:
|
||||
token = input("> ")
|
||||
if not len(token) >= 50:
|
||||
print("That doesn't look like a valid token.")
|
||||
token = None
|
||||
if token:
|
||||
await red._config.token.set(token)
|
||||
|
||||
if not prefix_set:
|
||||
prefix = ""
|
||||
print(
|
||||
"\nPick a prefix. A prefix is what you type before a "
|
||||
"command. Example:\n"
|
||||
"!help\n^ The exclamation mark is the prefix in this case.\n"
|
||||
"The prefix can be multiple characters. You will be able to change it "
|
||||
"later and add more of them.\nChoose your prefix:\n"
|
||||
)
|
||||
while not prefix:
|
||||
prefix = input("Prefix> ")
|
||||
if len(prefix) > 10:
|
||||
if not confirm("Your prefix seems overly long. Are you sure that it's correct?"):
|
||||
prefix = ""
|
||||
if prefix.startswith("/"):
|
||||
print(
|
||||
"Prefixes cannot start with '/', as it conflicts with Discord's slash commands."
|
||||
)
|
||||
prefix = ""
|
||||
if prefix:
|
||||
await red._config.prefix.set([prefix])
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def non_negative_int(arg: str) -> int:
|
||||
try:
|
||||
x = int(arg)
|
||||
except ValueError:
|
||||
raise argparse.ArgumentTypeError("The argument has to be a number.")
|
||||
if x < 0:
|
||||
raise argparse.ArgumentTypeError("The argument has to be a non-negative integer.")
|
||||
if x > sys.maxsize:
|
||||
raise argparse.ArgumentTypeError(
|
||||
f"The argument has to be lower than or equal to {sys.maxsize}."
|
||||
)
|
||||
return x
|
||||
|
||||
|
||||
def message_cache_size_int(arg: str) -> int:
|
||||
x = non_negative_int(arg)
|
||||
if x < 1000:
|
||||
raise argparse.ArgumentTypeError(
|
||||
"Message cache size has to be greater than or equal to 1000."
|
||||
)
|
||||
return x
|
||||
|
||||
|
||||
def parse_cli_flags(args):
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Red - Discord Bot", usage="redbot <instance_name> [arguments]"
|
||||
)
|
||||
parser.add_argument("--version", "-V", action="store_true", help="Show Red's current version")
|
||||
parser.add_argument("--debuginfo", action="store_true", help="Show debug information.")
|
||||
parser.add_argument(
|
||||
"--list-instances",
|
||||
action="store_true",
|
||||
help="List all instance names setup with 'redbot-setup'",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--edit",
|
||||
action="store_true",
|
||||
help="Edit the instance. This can be done without console interaction "
|
||||
"by passing --no-prompt and arguments that you want to change (available arguments: "
|
||||
"--edit-instance-name, --edit-data-path, --copy-data, --owner, --token, --prefix).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--edit-instance-name",
|
||||
type=str,
|
||||
help="New name for the instance. This argument only works with --edit argument passed.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--overwrite-existing-instance",
|
||||
action="store_true",
|
||||
help="Confirm overwriting of existing instance when changing name."
|
||||
" This argument only works with --edit argument passed.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--edit-data-path",
|
||||
type=str,
|
||||
help=(
|
||||
"New data path for the instance. This argument only works with --edit argument passed."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--copy-data",
|
||||
action="store_true",
|
||||
help="Copy data from old location. This argument only works "
|
||||
"with --edit and --edit-data-path arguments passed.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--owner",
|
||||
type=int,
|
||||
help="ID of the owner. Only who hosts "
|
||||
"Red should be owner, this has "
|
||||
"serious security implications if misused.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--co-owner",
|
||||
type=int,
|
||||
default=[],
|
||||
nargs="+",
|
||||
help="ID of a co-owner. Only people who have access "
|
||||
"to the system that is hosting Red should be "
|
||||
"co-owners, as this gives them complete access "
|
||||
"to the system's data. This has serious "
|
||||
"security implications if misused. Can be "
|
||||
"multiple.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--prefix", "-p", action="append", help="Global prefix. Can be multiple", default=[]
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-prompt",
|
||||
action="store_true",
|
||||
help="Disables console inputs. Features requiring "
|
||||
"console interaction could be disabled as a "
|
||||
"result",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-cogs", action="store_true", help="Starts Red with no cogs loaded, only core"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-cogs",
|
||||
type=str,
|
||||
nargs="+",
|
||||
help="Force loading specified cogs from the installed packages. "
|
||||
"Can be used with the --no-cogs flag to load these cogs exclusively.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--unload-cogs", type=str, nargs="+", help="Force unloading specified cogs."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Makes Red quit with code 0 just before the "
|
||||
"login. This is useful for testing the boot "
|
||||
"process.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
"--debug",
|
||||
action="count",
|
||||
default=0,
|
||||
dest="logging_level",
|
||||
help="Increase the verbosity of the logs, each usage of this flag increases the verbosity level by 1.",
|
||||
)
|
||||
parser.add_argument("--dev", action="store_true", help="Enables developer mode")
|
||||
parser.add_argument(
|
||||
"--mentionable",
|
||||
action="store_true",
|
||||
help="Allows mentioning the bot as an alternative to using the bot prefix",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rpc",
|
||||
action="store_true",
|
||||
help="Enables the built-in RPC server. Please read the docs prior to enabling this!",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rpc-port",
|
||||
type=int,
|
||||
default=6133,
|
||||
help="The port of the built-in RPC server to use. Default to 6133.",
|
||||
)
|
||||
parser.add_argument("--token", type=str, help="Run Red with the given token.")
|
||||
parser.add_argument(
|
||||
"--no-instance",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Run Red without any existing instance. "
|
||||
"The data will be saved under a temporary folder "
|
||||
"and deleted on next system restart."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"instance_name", nargs="?", help="Name of the bot instance created during `redbot-setup`."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--team-members-are-owners",
|
||||
action="store_true",
|
||||
dest="use_team_features",
|
||||
default=False,
|
||||
help=(
|
||||
"Treat application team members as owners. "
|
||||
"This is off by default. Owners can load and run arbitrary code. "
|
||||
"Do not enable if you would not trust all of your team members with "
|
||||
"all of the data on the host machine."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--message-cache-size",
|
||||
type=message_cache_size_int,
|
||||
default=1000,
|
||||
help="Set the maximum number of messages to store in the internal message cache.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-message-cache", action="store_true", help="Disable the internal message cache."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-intent",
|
||||
action="append",
|
||||
choices=list(discord.Intents.VALID_FLAGS), # DEP-WARN
|
||||
default=[],
|
||||
help="Unsupported flag that allows disabling the given intent."
|
||||
" Currently NOT SUPPORTED (and not covered by our version guarantees)"
|
||||
" as Red is not prepared to work without all intents.\n"
|
||||
f"Go to https://discordpy.readthedocs.io/en/v{discord_version}/api.html#discord.Intents"
|
||||
" to see what each intent does.\n"
|
||||
"This flag can be used multiple times to specify multiple intents.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--force-rich-logging",
|
||||
action="store_true",
|
||||
dest="rich_logging",
|
||||
default=None,
|
||||
help="Forcefully enables the Rich logging handlers. This is normally enabled for supported active terminals.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--force-disable-rich-logging",
|
||||
action="store_false",
|
||||
dest="rich_logging",
|
||||
default=None,
|
||||
help="Forcefully disables the Rich logging handlers.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rich-traceback-extra-lines",
|
||||
type=non_negative_int,
|
||||
default=0,
|
||||
help="Set the number of additional lines of code before and after the executed line"
|
||||
" that should be shown in tracebacks generated by Rich.\n"
|
||||
"Useful for development.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rich-traceback-show-locals",
|
||||
action="store_true",
|
||||
help="Enable showing local variables in tracebacks generated by Rich.\n"
|
||||
"Useful for development.",
|
||||
)
|
||||
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if args.prefix:
|
||||
args.prefix = sorted(args.prefix, reverse=True)
|
||||
else:
|
||||
args.prefix = []
|
||||
args.logging_level = cli_level_to_log_level(args.logging_level)
|
||||
|
||||
return args
|
||||
Reference in New Issue
Block a user