def _edit_instance_name(old_name, new_name, confirm_overwrite, no_prompt): if new_name: name = new_name if name in _get_instance_names() and not confirm_overwrite: name = old_name print( "An instance with this name already exists.\n" "If you want to remove the existing instance and replace it with this one," " run this command with --overwrite-existing-instance flag.") elif not no_prompt and confirm( "Would you like to change the instance name?", default=False): name = get_name() if name in _get_instance_names(): print("WARNING: An instance already exists with this name. " "Continuing will overwrite the existing instance config.") if not confirm( "Are you absolutely certain you want to continue with this instance name?", default=False, ): print("Instance name will remain unchanged.") name = old_name else: print("Instance name updated.") else: print("Instance name updated.") print() else: name = old_name return name
async def _edit_owner(red, owner, no_prompt): if owner: if not (15 <= len(str(owner)) <= 21): print( "The provided owner id doesn't look like a valid Discord user id." " Instance's owner will remain unchanged.") return await red._config.owner.set(owner) elif not no_prompt and confirm( "Would you like to change instance's owner?", default=False): print( "Remember:\n" "ONLY the person who is hosting Red should be owner." " This has SERIOUS security implications." " The owner can access any data that is present on the host system.\n" ) if confirm("Are you sure you want to change instance's owner?", default=False): print("Please enter a Discord user id for new owner:") while True: owner_id = input("> ").strip() if not (15 <= len(owner_id) <= 21 and owner_id.isdecimal()): print("That doesn't look like a valid Discord user id.") continue owner_id = int(owner_id) await red._config.owner.set(owner_id) print("Owner updated.") break else: print("Instance's owner will remain unchanged.") print()
async def _edit_token(red, token, no_prompt): if token: if not len(token) >= 50: print("The provided token doesn't look a valid Discord bot token." " Instance's token will remain unchanged.\n") return await red._config.token.set(token) elif not no_prompt and confirm( "Would you like to change instance's token?", default=False): await interactive_config(red, False, True, print_header=False) print("Token updated.\n")
def _edit_data_path(data, instance_name, data_path, copy_data, no_prompt): # This modifies the passed dict. if data_path: new_path = Path(data_path) try: exists = new_path.exists() except OSError: print("We were unable to check your chosen directory." " Provided path may contain an invalid character." " Data location will remain unchanged.") if not exists: try: new_path.mkdir(parents=True, exist_ok=True) except OSError: print("We were unable to create your chosen directory." " Data location will remain unchanged.") data["DATA_PATH"] = data_path if copy_data and not _copy_data(data): print( "Can't copy data to non-empty location. Data location will remain unchanged." ) data["DATA_PATH"] = data_manager.basic_config["DATA_PATH"] elif not no_prompt and confirm( "Would you like to change the data location?", default=False): data["DATA_PATH"] = get_data_dir(instance_name) if confirm("Do you want to copy the data from old location?", default=True): if not _copy_data(data): print("Can't copy the data to non-empty location.") if not confirm( "Do you still want to use the new data location?"): data["DATA_PATH"] = data_manager.basic_config["DATA_PATH"] print("Data location will remain unchanged.") return print("Old data has been copied over to the new location.") print("Data location updated.")
async def _edit_prefix(red, prefix, no_prompt): if prefix: prefixes = sorted(prefix, reverse=True) await red._config.prefix.set(prefixes) elif not no_prompt and confirm( "Would you like to change instance's prefixes?", default=False): print( "Enter the prefixes, separated by a space (please note " "that prefixes containing a space will need to be added with [p]set prefix)" ) while True: prefixes = input("> ").strip().split() if not prefixes: print("You need to pass at least one prefix!") continue prefixes = sorted(prefixes, reverse=True) await red._config.prefix.set(prefixes) print("Prefixes updated.\n") break
def get_name() -> str: name = "" while len(name) == 0: print("Please enter a name for your instance," " it will be used to run your bot from here on out.\n" "This name is case-sensitive and should only include characters" " A-z, numbers, underscores (_) and periods (.).") name = input("> ") if re.fullmatch(r"[A-Za-z0-9_\.\-]*", name) is None: print( "ERROR: Instance names can only include characters A-z, numbers, " "underscores (_) and periods (.).") name = "" elif "-" in name and not confirm( "Hyphens (-) in instance names may cause issues. Are you sure you want to continue with this instance name?", default=False, ): name = "" print() # new line for aesthetics return name
async def run_bot(red: Red, cli_flags: Namespace) -> None: """ This runs the bot. Any shutdown which is a result of not being able to log in needs to raise a SystemExit exception. If the bot starts normally, the bot should be left to handle the exit case. It will raise SystemExit in a task, which will reach the event loop and interrupt running forever, then trigger our cleanup process, and does not need additional handling in this function. """ driver_cls = drivers.get_driver_class() await driver_cls.initialize(**data_manager.storage_details()) beastbot.logback.init_logging( level=cli_flags.logging_level, location=data_manager.core_data_path() / "logs", force_rich_logging=cli_flags.rich_logging, ) log.debug("====Basic Config====") log.debug("Data Path: %s", data_manager._base_data_path()) log.debug("Storage Type: %s", data_manager.storage_type()) # lib folder has to be in sys.path before trying to load any 3rd-party cog (GH-3061) # We might want to change handling of requirements in Downloader at later date LIB_PATH = data_manager.cog_data_path(raw_name="Downloader") / "lib" LIB_PATH.mkdir(parents=True, exist_ok=True) if str(LIB_PATH) not in sys.path: sys.path.append(str(LIB_PATH)) # "It's important to note that the global `working_set` object is initialized from # `sys.path` when `pkg_resources` is first imported, but is only updated if you do # all future `sys.path` manipulation via `pkg_resources` APIs. If you manually modify # `sys.path`, you must invoke the appropriate methods on the `working_set` instance # to keep it in sync." # Source: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#workingset-objects pkg_resources.working_set.add_entry(str(LIB_PATH)) sys.meta_path.insert(0, SharedLibImportWarner()) if cli_flags.token: token = cli_flags.token else: token = os.environ.get("RED_TOKEN", None) if not token: token = await red._config.token() prefix = cli_flags.prefix or await red._config.prefix() if not (token and prefix): if cli_flags.no_prompt is False: new_token = await interactive_config(red, token_set=bool(token), prefix_set=bool(prefix)) if new_token: token = new_token else: log.critical("Token and prefix must be set in order to login.") sys.exit(1) if cli_flags.dry_run: await red.http.close() sys.exit(0) try: await red.start(token, bot=True, cli_flags=cli_flags) except discord.LoginFailure: log.critical("This token doesn't seem to be valid.") db_token = await red._config.token() if db_token and not cli_flags.no_prompt: if confirm("\nDo you want to reset the token?"): await red._config.token.set("") print("Token has been reset.") sys.exit(0) sys.exit(1) except discord.PrivilegedIntentsRequired: print( "Red requires all Privileged Intents to be enabled.\n" "You can find out how to enable Privileged Intents with this guide:\n" "https://docs.discord.red/en/stable/bot_application_guide.html#enabling-privileged-intents" ) sys.exit(1) return None