mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
[V3] NumPy Docstrings (#1032)
* ALL THE DOCSTRINGS * Remove imports in drivers package * Fixed build warnings
This commit is contained in:
parent
684004d614
commit
c80684a129
@ -37,6 +37,7 @@ extensions = [
|
|||||||
'sphinx.ext.autodoc',
|
'sphinx.ext.autodoc',
|
||||||
'sphinx.ext.intersphinx',
|
'sphinx.ext.intersphinx',
|
||||||
'sphinx.ext.viewcode',
|
'sphinx.ext.viewcode',
|
||||||
|
'sphinx.ext.napoleon',
|
||||||
'sphinxcontrib.asyncio'
|
'sphinxcontrib.asyncio'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -84,6 +85,9 @@ pygments_style = 'sphinx'
|
|||||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
todo_include_todos = False
|
todo_include_todos = False
|
||||||
|
|
||||||
|
# Role which is assigned when you make a simple reference within backticks
|
||||||
|
default_role = "any"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ----------------------------------------------
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -192,5 +192,4 @@ Driver Reference
|
|||||||
|
|
||||||
.. automodule:: redbot.core.drivers
|
.. automodule:: redbot.core.drivers
|
||||||
|
|
||||||
.. autoclass:: red_base.BaseDriver
|
.. autoclass:: redbot.core.drivers.red_base.BaseDriver
|
||||||
:members:
|
|
||||||
|
|||||||
@ -27,11 +27,15 @@ Installing Red
|
|||||||
|
|
||||||
1. Open a command prompt (open Start, search for "command prompt", then click it)
|
1. Open a command prompt (open Start, search for "command prompt", then click it)
|
||||||
2. Run the appropriate command, depending on if you want audio or not
|
2. Run the appropriate command, depending on if you want audio or not
|
||||||
|
|
||||||
* No audio: :code:`python -m pip install Red-DiscordBot`
|
* No audio: :code:`python -m pip install Red-DiscordBot`
|
||||||
* Audio: :code:`python -m pip install Red-DiscordBot[voice]`
|
* Audio: :code:`python -m pip install Red-DiscordBot[voice]`
|
||||||
|
|
||||||
3. Once that has completed, run :code:`redbot-setup` to set up your instance
|
3. Once that has completed, run :code:`redbot-setup` to set up your instance
|
||||||
|
|
||||||
* This will set the location where data will be stored, as well as your
|
* This will set the location where data will be stored, as well as your
|
||||||
storage backend and the name of the instance (which will be used for
|
storage backend and the name of the instance (which will be used for
|
||||||
running the bot)
|
running the bot)
|
||||||
|
|
||||||
4. Once done setting up the instance, run :code:`redbot <your instance name>` to run Red.
|
4. Once done setting up the instance, run :code:`redbot <your instance name>` to run Red.
|
||||||
It will walk through the initial setup, asking for your token and a prefix
|
It will walk through the initial setup, asking for your token and a prefix
|
||||||
@ -52,27 +52,37 @@ class Downloader:
|
|||||||
self._repo_manager = RepoManager(self.conf)
|
self._repo_manager = RepoManager(self.conf)
|
||||||
|
|
||||||
async def cog_install_path(self):
|
async def cog_install_path(self):
|
||||||
"""
|
"""Get the current cog install path.
|
||||||
Returns the current cog install path.
|
|
||||||
:return:
|
Returns
|
||||||
|
-------
|
||||||
|
pathlib.Path
|
||||||
|
The default cog install path.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return await self.bot.cog_mgr.install_path()
|
return await self.bot.cog_mgr.install_path()
|
||||||
|
|
||||||
async def installed_cogs(self) -> Tuple[Installable]:
|
async def installed_cogs(self) -> Tuple[Installable]:
|
||||||
"""
|
"""Get info on installed cogs.
|
||||||
Returns the dictionary mapping cog name to install location
|
|
||||||
and repo name.
|
Returns
|
||||||
:return:
|
-------
|
||||||
|
`tuple` of `Installable`
|
||||||
|
All installed cogs / shared lib directories.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
installed = await self.conf.installed()
|
installed = await self.conf.installed()
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
return tuple(Installable.from_json(v) for v in installed)
|
return tuple(Installable.from_json(v) for v in installed)
|
||||||
|
|
||||||
async def _add_to_installed(self, cog: Installable):
|
async def _add_to_installed(self, cog: Installable):
|
||||||
"""
|
"""Mark a cog as installed.
|
||||||
Marks a cog as installed.
|
|
||||||
:param cog:
|
Parameters
|
||||||
:return:
|
----------
|
||||||
|
cog : Installable
|
||||||
|
The cog to check off.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
installed = await self.conf.installed()
|
installed = await self.conf.installed()
|
||||||
cog_json = cog.to_json()
|
cog_json = cog.to_json()
|
||||||
@ -82,10 +92,13 @@ class Downloader:
|
|||||||
await self.conf.installed.set(installed)
|
await self.conf.installed.set(installed)
|
||||||
|
|
||||||
async def _remove_from_installed(self, cog: Installable):
|
async def _remove_from_installed(self, cog: Installable):
|
||||||
"""
|
"""Remove a cog from the saved list of installed cogs.
|
||||||
Removes a cog from the saved list of installed cogs.
|
|
||||||
:param cog:
|
Parameters
|
||||||
:return:
|
----------
|
||||||
|
cog : Installable
|
||||||
|
The cog to remove.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
installed = await self.conf.installed()
|
installed = await self.conf.installed()
|
||||||
cog_json = cog.to_json()
|
cog_json = cog.to_json()
|
||||||
@ -326,11 +339,19 @@ class Downloader:
|
|||||||
await ctx.send(box(msg))
|
await ctx.send(box(msg))
|
||||||
|
|
||||||
async def is_installed(self, cog_name: str) -> (bool, Union[Installable, None]):
|
async def is_installed(self, cog_name: str) -> (bool, Union[Installable, None]):
|
||||||
"""
|
"""Check to see if a cog has been installed through Downloader.
|
||||||
Checks to see if a cog with the given name was installed
|
|
||||||
through Downloader.
|
Parameters
|
||||||
:param cog_name:
|
----------
|
||||||
:return: is_installed, Installable
|
cog_name : str
|
||||||
|
The name of the cog to check for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`tuple` of (`bool`, `Installable`)
|
||||||
|
:code:`(True, Installable)` if the cog is installed, else
|
||||||
|
:code:`(False, None)`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for installable in await self.installed_cogs():
|
for installable in await self.installed_cogs():
|
||||||
if installable.name == cog_name:
|
if installable.name == cog_name:
|
||||||
@ -339,11 +360,20 @@ class Downloader:
|
|||||||
|
|
||||||
def format_findcog_info(self, command_name: str,
|
def format_findcog_info(self, command_name: str,
|
||||||
cog_installable: Union[Installable, object]=None) -> str:
|
cog_installable: Union[Installable, object]=None) -> str:
|
||||||
"""
|
"""Format a cog's info for output to discord.
|
||||||
Formats the info for output to discord
|
|
||||||
:param command_name:
|
Parameters
|
||||||
:param cog_installable: Can be an Installable instance or a Cog instance.
|
----------
|
||||||
:return: str
|
command_name : str
|
||||||
|
Name of the command which belongs to the cog.
|
||||||
|
cog_installable : `Installable` or `object`
|
||||||
|
Can be an `Installable` instance or a Cog instance.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
A formatted message for the user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(cog_installable, Installable):
|
if isinstance(cog_installable, Installable):
|
||||||
made_by = ", ".join(cog_installable.author) or _("Missing from info.json")
|
made_by = ", ".join(cog_installable.author) or _("Missing from info.json")
|
||||||
@ -360,12 +390,20 @@ class Downloader:
|
|||||||
return msg.format(command_name, made_by, repo_url, cog_name)
|
return msg.format(command_name, made_by, repo_url, cog_name)
|
||||||
|
|
||||||
def cog_name_from_instance(self, instance: object) -> str:
|
def cog_name_from_instance(self, instance: object) -> str:
|
||||||
"""
|
"""Determines the cog name that Downloader knows from the cog instance.
|
||||||
Determines the cog name that Downloader knows from the cog instance.
|
|
||||||
|
|
||||||
Probably.
|
Probably.
|
||||||
:param instance:
|
|
||||||
:return:
|
Parameters
|
||||||
|
----------
|
||||||
|
instance : object
|
||||||
|
The cog instance.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The name of the cog according to Downloader..
|
||||||
|
|
||||||
"""
|
"""
|
||||||
splitted = instance.__module__.split('.')
|
splitted = instance.__module__.split('.')
|
||||||
return splitted[-2]
|
return splitted[-2]
|
||||||
|
|||||||
@ -16,11 +16,38 @@ class InstallableType(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class Installable(RepoJSONMixin):
|
class Installable(RepoJSONMixin):
|
||||||
"""
|
"""Base class for anything the Downloader cog can install.
|
||||||
Base class for anything the Downloader cog can install.
|
|
||||||
- Modules
|
- Modules
|
||||||
- Repo Libraries
|
- Repo Libraries
|
||||||
- Other stuff?
|
- Other stuff?
|
||||||
|
|
||||||
|
The attributes of this class will mostly come from the installation's
|
||||||
|
info.json.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
repo_name : `str`
|
||||||
|
Name of the repository which this package belongs to.
|
||||||
|
author : `tuple` of `str`, optional
|
||||||
|
Name(s) of the author(s).
|
||||||
|
bot_version : `tuple` of `int`
|
||||||
|
The minimum bot version required for this installation. Right now
|
||||||
|
this is always :code:`3.0.0`.
|
||||||
|
hidden : `bool`
|
||||||
|
Whether or not this cog will be hidden from the user when they use
|
||||||
|
`Downloader`'s commands.
|
||||||
|
required_cogs : `dict`
|
||||||
|
In the form :code:`{cog_name : repo_url}`, these are cogs which are
|
||||||
|
required for this installation.
|
||||||
|
requirements : `tuple` of `str`
|
||||||
|
Required libraries for this installation.
|
||||||
|
tags : `tuple` of `str`
|
||||||
|
List of tags to assist in searching.
|
||||||
|
type : `int`
|
||||||
|
The type of this installation, as specified by
|
||||||
|
:class:`InstallationType`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
INFO_FILE_DESCRIPTION = """
|
INFO_FILE_DESCRIPTION = """
|
||||||
@ -28,10 +55,13 @@ class Installable(RepoJSONMixin):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, location: Path):
|
def __init__(self, location: Path):
|
||||||
"""
|
"""Base installable initializer.
|
||||||
Base installable initializer.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
location : pathlib.Path
|
||||||
|
Location (file or folder) to the installable.
|
||||||
|
|
||||||
:param location: Location (file or folder) to the installable.
|
|
||||||
"""
|
"""
|
||||||
super().__init__(location)
|
super().__init__(location)
|
||||||
|
|
||||||
@ -62,6 +92,7 @@ class Installable(RepoJSONMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""`str` : The name of this package."""
|
||||||
return self._location.stem
|
return self._location.stem
|
||||||
|
|
||||||
async def copy_to(self, target_dir: Path) -> bool:
|
async def copy_to(self, target_dir: Path) -> bool:
|
||||||
|
|||||||
@ -164,10 +164,13 @@ class Repo(RepoJSONMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def clone(self) -> Tuple[str]:
|
async def clone(self) -> Tuple[str]:
|
||||||
"""
|
"""Clone a new repo.
|
||||||
Clones a new repo.
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`tuple` of `str`
|
||||||
|
All available module names from this repo.
|
||||||
|
|
||||||
:return: List of available module names from this repo.
|
|
||||||
"""
|
"""
|
||||||
exists, path = self._existing_git_repo()
|
exists, path = self._existing_git_repo()
|
||||||
if exists:
|
if exists:
|
||||||
@ -202,10 +205,13 @@ class Repo(RepoJSONMixin):
|
|||||||
return self._update_available_modules()
|
return self._update_available_modules()
|
||||||
|
|
||||||
async def current_branch(self) -> str:
|
async def current_branch(self) -> str:
|
||||||
"""
|
"""Determine the current branch using git commands.
|
||||||
Determines the current branch using git commands.
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The current branch name.
|
||||||
|
|
||||||
:return: Current branch name
|
|
||||||
"""
|
"""
|
||||||
exists, _ = self._existing_git_repo()
|
exists, _ = self._existing_git_repo()
|
||||||
if not exists:
|
if not exists:
|
||||||
@ -226,11 +232,18 @@ class Repo(RepoJSONMixin):
|
|||||||
return p.stdout.decode().strip()
|
return p.stdout.decode().strip()
|
||||||
|
|
||||||
async def current_commit(self, branch: str=None) -> str:
|
async def current_commit(self, branch: str=None) -> str:
|
||||||
"""
|
"""Determine the current commit hash of the repo.
|
||||||
Determines the current commit hash of the repo.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
branch : `str`, optional
|
||||||
|
Override for repo's branch attribute.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The requested commit hash.
|
||||||
|
|
||||||
:param branch: Override for repo's branch attribute
|
|
||||||
:return: Commit hash string
|
|
||||||
"""
|
"""
|
||||||
if branch is None:
|
if branch is None:
|
||||||
branch = self.branch
|
branch = self.branch
|
||||||
@ -254,10 +267,13 @@ class Repo(RepoJSONMixin):
|
|||||||
return p.stdout.decode().strip()
|
return p.stdout.decode().strip()
|
||||||
|
|
||||||
async def hard_reset(self, branch: str=None) -> None:
|
async def hard_reset(self, branch: str=None) -> None:
|
||||||
"""
|
"""Perform a hard reset on the current repo.
|
||||||
Performs a hard reset on the current repo.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
branch : `str`, optional
|
||||||
|
Override for repo branch attribute.
|
||||||
|
|
||||||
:param branch: Override for repo branch attribute.
|
|
||||||
"""
|
"""
|
||||||
if branch is None:
|
if branch is None:
|
||||||
branch = self.branch
|
branch = self.branch
|
||||||
@ -281,11 +297,13 @@ class Repo(RepoJSONMixin):
|
|||||||
" the following path: {}".format(self.folder_path))
|
" the following path: {}".format(self.folder_path))
|
||||||
|
|
||||||
async def update(self) -> (str, str):
|
async def update(self) -> (str, str):
|
||||||
"""
|
"""Update the current branch of this repo.
|
||||||
Updates the current branch of this repo.
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`tuple` of `str`
|
||||||
|
:py:code`(old commit hash, new commit hash)`
|
||||||
|
|
||||||
:return: tuple of (old commit hash, new commit hash)
|
|
||||||
:rtype: tuple
|
|
||||||
"""
|
"""
|
||||||
curr_branch = await self.current_branch()
|
curr_branch = await self.current_branch()
|
||||||
old_commit = await self.current_commit(branch=curr_branch)
|
old_commit = await self.current_commit(branch=curr_branch)
|
||||||
@ -310,13 +328,20 @@ class Repo(RepoJSONMixin):
|
|||||||
return old_commit, new_commit
|
return old_commit, new_commit
|
||||||
|
|
||||||
async def install_cog(self, cog: Installable, target_dir: Path) -> bool:
|
async def install_cog(self, cog: Installable, target_dir: Path) -> bool:
|
||||||
"""
|
"""Install a cog to the target directory.
|
||||||
Copies a cog to the target directory.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cog : Installable
|
||||||
|
The package to install.
|
||||||
|
target_dir : pathlib.Path
|
||||||
|
The target directory for the cog installation.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
The success of the installation.
|
||||||
|
|
||||||
:param Installable cog: Cog to install.
|
|
||||||
:param pathlib.Path target_dir: Directory to install the cog in.
|
|
||||||
:return: Installation success status.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
if cog not in self.available_cogs:
|
if cog not in self.available_cogs:
|
||||||
raise DownloaderException("That cog does not exist in this repo")
|
raise DownloaderException("That cog does not exist in this repo")
|
||||||
@ -330,14 +355,23 @@ class Repo(RepoJSONMixin):
|
|||||||
return await cog.copy_to(target_dir=target_dir)
|
return await cog.copy_to(target_dir=target_dir)
|
||||||
|
|
||||||
async def install_libraries(self, target_dir: Path, libraries: Tuple[Installable]=()) -> bool:
|
async def install_libraries(self, target_dir: Path, libraries: Tuple[Installable]=()) -> bool:
|
||||||
"""
|
"""Install shared libraries to the target directory.
|
||||||
Copies all shared libraries (or a given subset) to the target
|
|
||||||
directory.
|
If :code:`libraries` is not specified, all shared libraries in the repo
|
||||||
|
will be installed.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
target_dir : pathlib.Path
|
||||||
|
Directory to install shared libraries to.
|
||||||
|
libraries : `tuple` of `Installable`
|
||||||
|
A subset of available libraries.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
The success of the installation.
|
||||||
|
|
||||||
:param pathlib.Path target_dir: Directory to install shared libraries to.
|
|
||||||
:param tuple(Installable) libraries: A subset of available libraries.
|
|
||||||
:return: Status of all installs.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
if libraries:
|
if libraries:
|
||||||
if not all([i in self.available_libraries for i in libraries]):
|
if not all([i in self.available_libraries for i in libraries]):
|
||||||
@ -350,15 +384,23 @@ class Repo(RepoJSONMixin):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
async def install_requirements(self, cog: Installable, target_dir: Path) -> bool:
|
async def install_requirements(self, cog: Installable, target_dir: Path) -> bool:
|
||||||
"""
|
"""Install a cog's requirements.
|
||||||
Installs the requirements defined by the requirements
|
|
||||||
attribute on the cog object and puts them in the given
|
Requirements will be installed via pip directly into
|
||||||
target directory.
|
:code:`target_dir`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cog : Installable
|
||||||
|
Cog for which to install requirements.
|
||||||
|
target_dir : pathlib.Path
|
||||||
|
Path to directory where requirements are to be installed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
Success of the installation.
|
||||||
|
|
||||||
:param Installable cog: Cog for which to install requirements.
|
|
||||||
:param pathlib.Path target_dir: Path to which to install requirements.
|
|
||||||
:return: Status of requirements install.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
if not target_dir.is_dir():
|
if not target_dir.is_dir():
|
||||||
raise ValueError("Target directory is not a directory.")
|
raise ValueError("Target directory is not a directory.")
|
||||||
@ -367,14 +409,20 @@ class Repo(RepoJSONMixin):
|
|||||||
return await self.install_raw_requirements(cog.requirements, target_dir)
|
return await self.install_raw_requirements(cog.requirements, target_dir)
|
||||||
|
|
||||||
async def install_raw_requirements(self, requirements: Tuple[str], target_dir: Path) -> bool:
|
async def install_raw_requirements(self, requirements: Tuple[str], target_dir: Path) -> bool:
|
||||||
"""
|
"""Install a list of requirements using pip.
|
||||||
Installs a list of requirements using pip and places them into
|
|
||||||
the given target directory.
|
Parameters
|
||||||
|
----------
|
||||||
|
requirements : `tuple` of `str`
|
||||||
|
List of requirement names to install via pip.
|
||||||
|
target_dir : pathlib.Path
|
||||||
|
Path to directory where requirements are to be installed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
Success of the installation
|
||||||
|
|
||||||
:param tuple(str) requirements: List of requirement names to install via pip.
|
|
||||||
:param pathlib.Path target_dir: Directory to install requirements to.
|
|
||||||
:return: Status of all requirements install.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
if len(requirements) == 0:
|
if len(requirements) == 0:
|
||||||
return True
|
return True
|
||||||
@ -398,10 +446,9 @@ class Repo(RepoJSONMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def available_cogs(self) -> Tuple[Installable]:
|
def available_cogs(self) -> Tuple[Installable]:
|
||||||
"""
|
"""`tuple` of `installable` : All available cogs in this Repo.
|
||||||
Returns a list of available cogs (not shared libraries and not hidden).
|
|
||||||
|
|
||||||
:rtype: tuple(Installable)
|
This excludes hidden or shared packages.
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
return tuple(
|
return tuple(
|
||||||
@ -411,10 +458,8 @@ class Repo(RepoJSONMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def available_libraries(self) -> Tuple[Installable]:
|
def available_libraries(self) -> Tuple[Installable]:
|
||||||
"""
|
"""`tuple` of `installable` : All available shared libraries in this
|
||||||
Returns a list of available shared libraries in this repo.
|
Repo.
|
||||||
|
|
||||||
:rtype: tuple(Installable)
|
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
return tuple(
|
return tuple(
|
||||||
@ -463,14 +508,22 @@ class RepoManager:
|
|||||||
return name.lower()
|
return name.lower()
|
||||||
|
|
||||||
async def add_repo(self, url: str, name: str, branch: str="master") -> Repo:
|
async def add_repo(self, url: str, name: str, branch: str="master") -> Repo:
|
||||||
"""
|
"""Add and clone a git repository.
|
||||||
Adds a repo and clones it.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
url : str
|
||||||
|
URL to the git repository.
|
||||||
|
name : str
|
||||||
|
Internal name of the repository.
|
||||||
|
branch : str
|
||||||
|
Name of the default branch to checkout into.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Repo
|
||||||
|
New Repo object representing the cloned repository.
|
||||||
|
|
||||||
:param url: URL of git repo to clone.
|
|
||||||
:param name: Internal name of repo.
|
|
||||||
:param branch: Branch to clone.
|
|
||||||
:return: New repo object representing cloned repo.
|
|
||||||
:rtype: Repo
|
|
||||||
"""
|
"""
|
||||||
name = self.validate_and_normalize_repo_name(name)
|
name = self.validate_and_normalize_repo_name(name)
|
||||||
if self.does_repo_exist(name):
|
if self.does_repo_exist(name):
|
||||||
@ -490,30 +543,45 @@ class RepoManager:
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
def get_repo(self, name: str) -> Union[Repo, None]:
|
def get_repo(self, name: str) -> Union[Repo, None]:
|
||||||
"""
|
"""Get a Repo object for a repository.
|
||||||
Returns a repo object with the given name.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The name of the repository to retrieve.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`Repo` or `None`
|
||||||
|
Repo object for the repository, if it exists.
|
||||||
|
|
||||||
:param name: Repo name
|
|
||||||
:return: Repo object or ``None`` if repo does not exist.
|
|
||||||
:rtype: Union[Repo, None]
|
|
||||||
"""
|
"""
|
||||||
return self._repos.get(name, None)
|
return self._repos.get(name, None)
|
||||||
|
|
||||||
def get_all_repo_names(self) -> Tuple[str]:
|
def get_all_repo_names(self) -> Tuple[str]:
|
||||||
"""
|
"""Get all repo names.
|
||||||
Returns a tuple of all repo names
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`tuple` of `str`
|
||||||
|
|
||||||
:rtype: tuple(str)
|
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
return tuple(self._repos.keys())
|
return tuple(self._repos.keys())
|
||||||
|
|
||||||
async def delete_repo(self, name: str):
|
async def delete_repo(self, name: str):
|
||||||
"""
|
"""Delete a repository and its folders.
|
||||||
Deletes a repo and its folders with the given name.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The name of the repository to delete.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
MissingGitRepo
|
||||||
|
If the repo does not exist.
|
||||||
|
|
||||||
:param name: Name of the repo to delete.
|
|
||||||
:raises MissingGitRepo: If the repo does not exist.
|
|
||||||
"""
|
"""
|
||||||
repo = self.get_repo(name)
|
repo = self.get_repo(name)
|
||||||
if repo is None:
|
if repo is None:
|
||||||
@ -529,12 +597,14 @@ class RepoManager:
|
|||||||
await self._save_repos()
|
await self._save_repos()
|
||||||
|
|
||||||
async def update_all_repos(self) -> MutableMapping[Repo, Tuple[str, str]]:
|
async def update_all_repos(self) -> MutableMapping[Repo, Tuple[str, str]]:
|
||||||
"""
|
"""Call `Repo.update` on all repositories.
|
||||||
Calls :py:meth:`Repo.update` on all repos.
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
A mapping of `Repo` objects that received new commits to a `tuple`
|
||||||
|
of `str` containing old and new commit hashes.
|
||||||
|
|
||||||
:return:
|
|
||||||
A mapping of :py:class:`Repo` objects that received new commits to a tuple containing old and
|
|
||||||
new commit hashes.
|
|
||||||
"""
|
"""
|
||||||
ret = {}
|
ret = {}
|
||||||
for _, repo in self._repos.items():
|
for _, repo in self._repos.items():
|
||||||
|
|||||||
@ -37,7 +37,9 @@ _bank_type = type("Bank", (object,), {})
|
|||||||
|
|
||||||
|
|
||||||
class Account:
|
class Account:
|
||||||
"""A single account. This class should ONLY be instantiated by the bank itself."""
|
"""A single account.
|
||||||
|
|
||||||
|
This class should ONLY be instantiated by the bank itself."""
|
||||||
|
|
||||||
def __init__(self, name: str, balance: int, created_at: datetime.datetime):
|
def __init__(self, name: str, balance: int, created_at: datetime.datetime):
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -57,60 +59,87 @@ if not os.environ.get('BUILDING_DOCS'):
|
|||||||
|
|
||||||
|
|
||||||
def _encoded_current_time() -> int:
|
def _encoded_current_time() -> int:
|
||||||
"""
|
"""Get the current UTC time as a timestamp.
|
||||||
Encoded current timestamp in UTC.
|
|
||||||
:return:
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
The current UTC timestamp.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.utcnow()
|
||||||
return _encode_time(now)
|
return _encode_time(now)
|
||||||
|
|
||||||
|
|
||||||
def _encode_time(time: datetime.datetime) -> int:
|
def _encode_time(time: datetime.datetime) -> int:
|
||||||
"""
|
"""Convert a datetime object to a serializable int.
|
||||||
Goes from datetime object to serializable int.
|
|
||||||
:param time:
|
Parameters
|
||||||
:return:
|
----------
|
||||||
|
time : datetime.datetime
|
||||||
|
The datetime to convert.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
The timestamp of the datetime object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ret = int(time.timestamp())
|
ret = int(time.timestamp())
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def _decode_time(time: int) -> datetime.datetime:
|
def _decode_time(time: int) -> datetime.datetime:
|
||||||
"""
|
"""Convert a timestamp to a datetime object.
|
||||||
Returns decoded timestamp in UTC.
|
|
||||||
:param time:
|
Parameters
|
||||||
:return:
|
----------
|
||||||
|
time : int
|
||||||
|
The timestamp to decode.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
datetime.datetime
|
||||||
|
The datetime object from the timestamp.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return datetime.datetime.utcfromtimestamp(time)
|
return datetime.datetime.utcfromtimestamp(time)
|
||||||
|
|
||||||
|
|
||||||
async def get_balance(member: discord.Member) -> int:
|
async def get_balance(member: discord.Member) -> int:
|
||||||
"""
|
"""Get the current balance of a member.
|
||||||
Gets the current balance of a member.
|
|
||||||
|
|
||||||
:param discord.Member member:
|
Parameters
|
||||||
|
----------
|
||||||
|
member : discord.Member
|
||||||
The member whose balance to check.
|
The member whose balance to check.
|
||||||
:return:
|
|
||||||
The member's balance
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
int
|
int
|
||||||
|
The member's balance
|
||||||
|
|
||||||
"""
|
"""
|
||||||
acc = await get_account(member)
|
acc = await get_account(member)
|
||||||
return acc.balance
|
return acc.balance
|
||||||
|
|
||||||
|
|
||||||
async def can_spend(member: discord.Member, amount: int) -> bool:
|
async def can_spend(member: discord.Member, amount: int) -> bool:
|
||||||
"""
|
"""Determine if a member can spend the given amount.
|
||||||
Determines if a member can spend the given amount.
|
|
||||||
|
|
||||||
:param discord.Member member:
|
Parameters
|
||||||
|
----------
|
||||||
|
member : discord.Member
|
||||||
The member wanting to spend.
|
The member wanting to spend.
|
||||||
:param int amount:
|
amount : int
|
||||||
The amount the member wants to spend.
|
The amount the member wants to spend.
|
||||||
:return:
|
|
||||||
:code:`True` if the member has a sufficient balance to spend the amount, else :code:`False`.
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
bool
|
bool
|
||||||
|
:code:`True` if the member has a sufficient balance to spend the
|
||||||
|
amount, else :code:`False`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if _invalid_amount(amount):
|
if _invalid_amount(amount):
|
||||||
return False
|
return False
|
||||||
@ -118,21 +147,25 @@ async def can_spend(member: discord.Member, amount: int) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
async def set_balance(member: discord.Member, amount: int) -> int:
|
async def set_balance(member: discord.Member, amount: int) -> int:
|
||||||
"""
|
"""Set an account balance.
|
||||||
Sets an account balance.
|
|
||||||
|
|
||||||
May raise ValueError if amount is invalid.
|
Parameters
|
||||||
|
----------
|
||||||
:param discord.Member member:
|
member : discord.Member
|
||||||
The member whose balance to set.
|
The member whose balance to set.
|
||||||
:param int amount:
|
amount : int
|
||||||
The amount to set the balance to.
|
The amount to set the balance to.
|
||||||
:return:
|
|
||||||
New account balance.
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
int
|
int
|
||||||
:raises ValueError:
|
New account balance.
|
||||||
If attempting to set the balance to a negative number
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
If attempting to set the balance to a negative number.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if amount < 0:
|
if amount < 0:
|
||||||
raise ValueError("Not allowed to have negative balance.")
|
raise ValueError("Not allowed to have negative balance.")
|
||||||
@ -157,22 +190,26 @@ def _invalid_amount(amount: int) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
async def withdraw_credits(member: discord.Member, amount: int) -> int:
|
async def withdraw_credits(member: discord.Member, amount: int) -> int:
|
||||||
"""
|
"""Remove a certain amount of credits from an account.
|
||||||
Removes a certain amount of credits from an account.
|
|
||||||
|
|
||||||
May raise ValueError if the amount is invalid or if the account has
|
Parameters
|
||||||
insufficient funds.
|
----------
|
||||||
|
member : discord.Member
|
||||||
:param discord.Member member:
|
|
||||||
The member to withdraw credits from.
|
The member to withdraw credits from.
|
||||||
:param int amount:
|
amount : int
|
||||||
The amount to withdraw.
|
The amount to withdraw.
|
||||||
:return:
|
|
||||||
New account balance.
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
int
|
int
|
||||||
:raises ValueError:
|
New account balance.
|
||||||
if the withdrawal amount is invalid or if the account has insufficient funds
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
If the withdrawal amount is invalid or if the account has insufficient
|
||||||
|
funds.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if _invalid_amount(amount):
|
if _invalid_amount(amount):
|
||||||
raise ValueError("Invalid withdrawal amount {} <= 0".format(amount))
|
raise ValueError("Invalid withdrawal amount {} <= 0".format(amount))
|
||||||
@ -185,21 +222,25 @@ async def withdraw_credits(member: discord.Member, amount: int) -> int:
|
|||||||
|
|
||||||
|
|
||||||
async def deposit_credits(member: discord.Member, amount: int) -> int:
|
async def deposit_credits(member: discord.Member, amount: int) -> int:
|
||||||
"""
|
"""Add a given amount of credits to an account.
|
||||||
Adds a given amount of credits to an account.
|
|
||||||
|
|
||||||
May raise ValueError if the amount is invalid.
|
Parameters
|
||||||
|
----------
|
||||||
:param discord.Member member:
|
member : discord.Member
|
||||||
The member to deposit credits to.
|
The member to deposit credits to.
|
||||||
:param int amount:
|
amount : int
|
||||||
The amount to deposit.
|
The amount to deposit.
|
||||||
:return:
|
|
||||||
The new balance
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
int
|
int
|
||||||
:raises ValueError:
|
The new balance.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
If the deposit amount is invalid.
|
If the deposit amount is invalid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if _invalid_amount(amount):
|
if _invalid_amount(amount):
|
||||||
raise ValueError("Invalid withdrawal amount {} <= 0".format(amount))
|
raise ValueError("Invalid withdrawal amount {} <= 0".format(amount))
|
||||||
@ -209,24 +250,27 @@ async def deposit_credits(member: discord.Member, amount: int) -> int:
|
|||||||
|
|
||||||
|
|
||||||
async def transfer_credits(from_: discord.Member, to: discord.Member, amount: int):
|
async def transfer_credits(from_: discord.Member, to: discord.Member, amount: int):
|
||||||
"""
|
"""Transfer a given amount of credits from one account to another.
|
||||||
Transfers a given amount of credits from one account to another.
|
|
||||||
|
|
||||||
May raise ValueError if the amount is invalid or if the :code:`from_`
|
Parameters
|
||||||
account has insufficient funds.
|
----------
|
||||||
|
from_: discord.Member
|
||||||
:param discord.Member from_:
|
|
||||||
The member to transfer from.
|
The member to transfer from.
|
||||||
:param discord.Member to:
|
to : discord.Member
|
||||||
The member to transfer to.
|
The member to transfer to.
|
||||||
:param int amount:
|
amount : int
|
||||||
The amount to transfer.
|
The amount to transfer.
|
||||||
:return:
|
|
||||||
The new balance.
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
int
|
int
|
||||||
:raises ValueError:
|
The new balance.
|
||||||
If the amount is invalid or if :code:`from_` has insufficient funds.
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
If the amount is invalid or if ``from_`` has insufficient funds.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if _invalid_amount(amount):
|
if _invalid_amount(amount):
|
||||||
raise ValueError("Invalid transfer amount {} <= 0".format(amount))
|
raise ValueError("Invalid transfer amount {} <= 0".format(amount))
|
||||||
@ -236,18 +280,18 @@ async def transfer_credits(from_: discord.Member, to: discord.Member, amount: in
|
|||||||
|
|
||||||
|
|
||||||
async def wipe_bank(user: Union[discord.User, discord.Member]):
|
async def wipe_bank(user: Union[discord.User, discord.Member]):
|
||||||
"""
|
"""Delete all accounts from the bank.
|
||||||
Deletes all accounts from the bank.
|
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
|
|
||||||
A member is required if the bank is currently guild specific.
|
A member is required if the bank is currently guild specific.
|
||||||
|
|
||||||
:param user:
|
Parameters
|
||||||
|
----------
|
||||||
|
user : `discord.User` or `discord.Member`
|
||||||
A user to be used in clearing the bank, this is required for technical
|
A user to be used in clearing the bank, this is required for technical
|
||||||
reasons and it does not matter which user/member is used.
|
reasons and it does not matter which user/member is used.
|
||||||
:type user:
|
|
||||||
discord.User or discord.Member
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
await _conf.user(user).clear()
|
await _conf.user(user).clear()
|
||||||
@ -256,19 +300,23 @@ async def wipe_bank(user: Union[discord.User, discord.Member]):
|
|||||||
|
|
||||||
|
|
||||||
async def get_guild_accounts(guild: discord.Guild) -> List[Account]:
|
async def get_guild_accounts(guild: discord.Guild) -> List[Account]:
|
||||||
"""
|
"""Get all account data for the given guild.
|
||||||
Gets all account data for the given guild.
|
|
||||||
|
|
||||||
May raise RuntimeError if the bank is currently global.
|
Parameters
|
||||||
|
----------
|
||||||
:param discord.Guild guild:
|
guild : discord.Guild
|
||||||
The guild to get accounts for.
|
The guild to get accounts for.
|
||||||
:return:
|
|
||||||
A generator for all guild accounts.
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
generator
|
`list` of `Account`
|
||||||
:raises RuntimeError:
|
A list of all guild accounts.
|
||||||
If the bank is global.
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If the bank is currently global.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if is_global():
|
if is_global():
|
||||||
raise RuntimeError("The bank is currently global.")
|
raise RuntimeError("The bank is currently global.")
|
||||||
@ -283,19 +331,23 @@ async def get_guild_accounts(guild: discord.Guild) -> List[Account]:
|
|||||||
|
|
||||||
|
|
||||||
async def get_global_accounts(user: discord.User) -> List[Account]:
|
async def get_global_accounts(user: discord.User) -> List[Account]:
|
||||||
"""
|
"""Get all global account data.
|
||||||
Gets all global account data.
|
|
||||||
|
|
||||||
May raise RuntimeError if the bank is currently guild specific.
|
Parameters
|
||||||
|
----------
|
||||||
:param discord.User user:
|
user : discord.User
|
||||||
A user to be used for getting accounts.
|
A user to be used for getting accounts.
|
||||||
:return:
|
|
||||||
A generator of all global accounts.
|
Returns
|
||||||
:rtype:
|
-------
|
||||||
generator
|
`list` of `Account`
|
||||||
:raises RuntimeError:
|
A list of all global accounts.
|
||||||
If the bank is guild specific.
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If the bank is currently guild specific.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not is_global():
|
if not is_global():
|
||||||
raise RuntimeError("The bank is not currently global.")
|
raise RuntimeError("The bank is not currently global.")
|
||||||
@ -311,18 +363,20 @@ async def get_global_accounts(user: discord.User) -> List[Account]:
|
|||||||
|
|
||||||
|
|
||||||
async def get_account(member: Union[discord.Member, discord.User]) -> Account:
|
async def get_account(member: Union[discord.Member, discord.User]) -> Account:
|
||||||
"""
|
"""Get the appropriate account for the given user or member.
|
||||||
Gets the appropriate account for the given user or member. A member is
|
|
||||||
required if the bank is currently guild specific.
|
|
||||||
|
|
||||||
:param member:
|
A member is required if the bank is currently guild specific.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
member : `discord.User` or `discord.Member`
|
||||||
The user whose account to get.
|
The user whose account to get.
|
||||||
:type member:
|
|
||||||
discord.User or discord.Member
|
Returns
|
||||||
:return:
|
-------
|
||||||
|
Account
|
||||||
The user's account.
|
The user's account.
|
||||||
:rtype:
|
|
||||||
:py:class:`Account`
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
acc_data = (await _conf.user(member)()).copy()
|
acc_data = (await _conf.user(member)()).copy()
|
||||||
@ -344,36 +398,43 @@ async def get_account(member: Union[discord.Member, discord.User]) -> Account:
|
|||||||
|
|
||||||
|
|
||||||
async def is_global() -> bool:
|
async def is_global() -> bool:
|
||||||
"""
|
"""Determine if the bank is currently global.
|
||||||
Determines if the bank is currently global.
|
|
||||||
|
|
||||||
:return:
|
Returns
|
||||||
:code:`True` if the bank is global, otherwise :code:`False`.
|
-------
|
||||||
:rtype:
|
|
||||||
bool
|
bool
|
||||||
|
:code:`True` if the bank is global, otherwise :code:`False`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return await _conf.is_global()
|
return await _conf.is_global()
|
||||||
|
|
||||||
|
|
||||||
async def set_global(global_: bool, user: Union[discord.User, discord.Member]) -> bool:
|
async def set_global(global_: bool, user: Union[discord.User, discord.Member]) -> bool:
|
||||||
"""
|
"""Set global status of the bank.
|
||||||
Sets global status of the bank, requires the user parameter for technical reasons.
|
|
||||||
|
Requires the user parameter for technical reasons.
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
|
|
||||||
All accounts are reset when you switch!
|
All accounts are reset when you switch!
|
||||||
|
|
||||||
:param global_:
|
Parameters
|
||||||
|
----------
|
||||||
|
global_ : bool
|
||||||
:code:`True` will set bank to global mode.
|
:code:`True` will set bank to global mode.
|
||||||
:param user:
|
user : `discord.User` or `discord.Member`
|
||||||
Must be a Member object if changing TO global mode.
|
Must be a Member object if changing TO global mode.
|
||||||
:type user:
|
|
||||||
discord.User or discord.Member
|
Returns
|
||||||
:return:
|
-------
|
||||||
New bank mode, :code:`True` is global.
|
|
||||||
:rtype:
|
|
||||||
bool
|
bool
|
||||||
:raises RuntimeError:
|
New bank mode, :code:`True` is global.
|
||||||
If bank is becoming global and :py:class:`discord.Member` was not provided.
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If bank is becoming global and a `discord.Member` was not provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if (await is_global()) is global_:
|
if (await is_global()) is global_:
|
||||||
return global_
|
return global_
|
||||||
@ -391,20 +452,24 @@ async def set_global(global_: bool, user: Union[discord.User, discord.Member]) -
|
|||||||
|
|
||||||
|
|
||||||
async def get_bank_name(guild: discord.Guild=None) -> str:
|
async def get_bank_name(guild: discord.Guild=None) -> str:
|
||||||
"""
|
"""Get the current bank name.
|
||||||
Gets the current bank name. If the bank is guild-specific the
|
|
||||||
guild parameter is required.
|
|
||||||
|
|
||||||
May raise RuntimeError if guild is missing and required.
|
Parameters
|
||||||
|
----------
|
||||||
|
guild : `discord.Guild`, optional
|
||||||
|
The guild to get the bank name for (required if bank is
|
||||||
|
guild-specific).
|
||||||
|
|
||||||
:param discord.Guild guild:
|
Returns
|
||||||
The guild to get the bank name for (required if bank is guild-specific).
|
-------
|
||||||
:return:
|
|
||||||
The bank's name.
|
|
||||||
:rtype:
|
|
||||||
str
|
str
|
||||||
:raises RuntimeError:
|
The bank's name.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
If the bank is guild-specific and guild was not provided.
|
If the bank is guild-specific and guild was not provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
return await _conf.bank_name()
|
return await _conf.bank_name()
|
||||||
@ -415,22 +480,26 @@ async def get_bank_name(guild: discord.Guild=None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
async def set_bank_name(name: str, guild: discord.Guild=None) -> str:
|
async def set_bank_name(name: str, guild: discord.Guild=None) -> str:
|
||||||
"""
|
"""Set the bank name.
|
||||||
Sets the bank name, if bank is guild specific the guild parameter is
|
|
||||||
required.
|
|
||||||
|
|
||||||
May throw RuntimeError if guild is required and missing.
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The new name for the bank.
|
||||||
|
guild : `discord.Guild`, optional
|
||||||
|
The guild to set the bank name for (required if bank is
|
||||||
|
guild-specific).
|
||||||
|
|
||||||
:param str name:
|
Returns
|
||||||
The new name for the bank.
|
-------
|
||||||
:param discord.Guild guild:
|
|
||||||
The guild to set the bank name for (required if bank is guild-specific).
|
|
||||||
:return:
|
|
||||||
The new name for the bank.
|
|
||||||
:rtype:
|
|
||||||
str
|
str
|
||||||
:raises RuntimeError:
|
The new name for the bank.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
If the bank is guild-specific and guild was not provided.
|
If the bank is guild-specific and guild was not provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
await _conf.bank_name.set(name)
|
await _conf.bank_name.set(name)
|
||||||
@ -443,20 +512,24 @@ async def set_bank_name(name: str, guild: discord.Guild=None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
async def get_currency_name(guild: discord.Guild=None) -> str:
|
async def get_currency_name(guild: discord.Guild=None) -> str:
|
||||||
"""
|
"""Get the currency name of the bank.
|
||||||
Gets the currency name of the bank. The guild parameter is required if
|
|
||||||
the bank is guild-specific.
|
|
||||||
|
|
||||||
May raise RuntimeError if the guild is missing and required.
|
Parameters
|
||||||
|
----------
|
||||||
|
guild : `discord.Guild`, optional
|
||||||
|
The guild to get the currency name for (required if bank is
|
||||||
|
guild-specific).
|
||||||
|
|
||||||
:param discord.Guild guild:
|
Returns
|
||||||
The guild to get the currency name for (required if bank is guild-specific).
|
-------
|
||||||
:return:
|
|
||||||
The currency name.
|
|
||||||
:rtype:
|
|
||||||
str
|
str
|
||||||
:raises RuntimeError:
|
The currency name.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
If the bank is guild-specific and guild was not provided.
|
If the bank is guild-specific and guild was not provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
return await _conf.currency()
|
return await _conf.currency()
|
||||||
@ -467,22 +540,26 @@ async def get_currency_name(guild: discord.Guild=None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
async def set_currency_name(name: str, guild: discord.Guild=None) -> str:
|
async def set_currency_name(name: str, guild: discord.Guild=None) -> str:
|
||||||
"""
|
"""Set the currency name for the bank.
|
||||||
Sets the currency name for the bank, if bank is guild specific the
|
|
||||||
guild parameter is required.
|
|
||||||
|
|
||||||
May raise RuntimeError if guild is missing and required.
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The new name for the currency.
|
||||||
|
guild : `discord.Guild`, optional
|
||||||
|
The guild to set the currency name for (required if bank is
|
||||||
|
guild-specific).
|
||||||
|
|
||||||
:param str name:
|
Returns
|
||||||
The new name for the currency.
|
-------
|
||||||
:param discord.Guild guild:
|
|
||||||
The guild to set the currency name for (required if bank is guild-specific).
|
|
||||||
:return:
|
|
||||||
The new name for the currency.
|
|
||||||
:rtype:
|
|
||||||
str
|
str
|
||||||
:raises RuntimeError:
|
The new name for the currency.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
If the bank is guild-specific and guild was not provided.
|
If the bank is guild-specific and guild was not provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
await _conf.currency.set(name)
|
await _conf.currency.set(name)
|
||||||
@ -495,20 +572,24 @@ async def set_currency_name(name: str, guild: discord.Guild=None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
async def get_default_balance(guild: discord.Guild=None) -> int:
|
async def get_default_balance(guild: discord.Guild=None) -> int:
|
||||||
"""
|
"""Get the current default balance amount.
|
||||||
Gets the current default balance amount. If the bank is guild-specific
|
|
||||||
you must pass guild.
|
|
||||||
|
|
||||||
May raise RuntimeError if guild is missing and required.
|
Parameters
|
||||||
|
----------
|
||||||
|
guild : `discord.Guild`, optional
|
||||||
|
The guild to get the default balance for (required if bank is
|
||||||
|
guild-specific).
|
||||||
|
|
||||||
:param discord.Guild guild:
|
Returns
|
||||||
The guild to get the default balance for (required if bank is guild-specific).
|
-------
|
||||||
:return:
|
int
|
||||||
The bank's default balance.
|
The bank's default balance.
|
||||||
:rtype:
|
|
||||||
str
|
Raises
|
||||||
:raises RuntimeError:
|
------
|
||||||
|
RuntimeError
|
||||||
If the bank is guild-specific and guild was not provided.
|
If the bank is guild-specific and guild was not provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
return await _conf.default_balance()
|
return await _conf.default_balance()
|
||||||
@ -519,26 +600,28 @@ async def get_default_balance(guild: discord.Guild=None) -> int:
|
|||||||
|
|
||||||
|
|
||||||
async def set_default_balance(amount: int, guild: discord.Guild=None) -> int:
|
async def set_default_balance(amount: int, guild: discord.Guild=None) -> int:
|
||||||
"""
|
"""Set the default balance amount.
|
||||||
Sets the default balance amount. Guild is required if the bank is
|
|
||||||
guild-specific.
|
|
||||||
|
|
||||||
May raise RuntimeError if guild is missing and required.
|
Parameters
|
||||||
|
----------
|
||||||
May raise ValueError if amount is invalid.
|
amount : int
|
||||||
|
|
||||||
:param int amount:
|
|
||||||
The new default balance.
|
The new default balance.
|
||||||
:param discord.Guild guild:
|
guild : `discord.Guild`, optional
|
||||||
The guild to set the default balance for (required if bank is guild-specific).
|
The guild to set the default balance for (required if bank is
|
||||||
:return:
|
guild-specific).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
The new default balance.
|
The new default balance.
|
||||||
:rtype:
|
|
||||||
str
|
Raises
|
||||||
:raises RuntimeError:
|
------
|
||||||
|
RuntimeError
|
||||||
If the bank is guild-specific and guild was not provided.
|
If the bank is guild-specific and guild was not provided.
|
||||||
:raises ValueError:
|
ValueError
|
||||||
If the amount is invalid.
|
If the amount is invalid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
amount = int(amount)
|
amount = int(amount)
|
||||||
if amount < 0:
|
if amount < 0:
|
||||||
|
|||||||
@ -14,6 +14,14 @@ from . import Config, i18n, RedContext
|
|||||||
|
|
||||||
|
|
||||||
class RedBase(BotBase):
|
class RedBase(BotBase):
|
||||||
|
"""Mixin for the main bot class.
|
||||||
|
|
||||||
|
This exists because `Red` inherits from `discord.AutoShardedClient`, which
|
||||||
|
is something other bot classes (namely selfbots) may not want to have as
|
||||||
|
a parent class.
|
||||||
|
|
||||||
|
Selfbots should inherit from this mixin along with `discord.Client`.
|
||||||
|
"""
|
||||||
def __init__(self, cli_flags, bot_dir: Path=Path.cwd(), **kwargs):
|
def __init__(self, cli_flags, bot_dir: Path=Path.cwd(), **kwargs):
|
||||||
self._shutdown_mode = ExitCodes.CRITICAL
|
self._shutdown_mode = ExitCodes.CRITICAL
|
||||||
self.db = Config.get_core_conf(force_registration=True)
|
self.db = Config.get_core_conf(force_registration=True)
|
||||||
@ -168,11 +176,18 @@ class Red(RedBase, discord.AutoShardedClient):
|
|||||||
"""
|
"""
|
||||||
You're welcome Caleb.
|
You're welcome Caleb.
|
||||||
"""
|
"""
|
||||||
async def shutdown(self, *, restart=False):
|
async def shutdown(self, *, restart: bool=False):
|
||||||
"""Gracefully quits Red with exit code 0
|
"""Gracefully quit Red.
|
||||||
|
|
||||||
If restart is True, the exit code will be 26 instead
|
The program will exit with code :code:`0` by default.
|
||||||
Upon receiving that exit code, the launcher restarts Red"""
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
restart : bool
|
||||||
|
If :code:`True`, the program will exit with code :code:`26`. If the
|
||||||
|
launcher sees this, it will attempt to restart the bot.
|
||||||
|
|
||||||
|
"""
|
||||||
if not restart:
|
if not restart:
|
||||||
self._shutdown_mode = ExitCodes.SHUTDOWN
|
self._shutdown_mode = ExitCodes.SHUTDOWN
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -16,10 +16,12 @@ __all__ = ["CogManager"]
|
|||||||
|
|
||||||
|
|
||||||
class CogManager:
|
class CogManager:
|
||||||
"""
|
"""Directory manager for Red's cogs.
|
||||||
This module allows you to load cogs from multiple directories and even from outside the bot
|
|
||||||
directory. You may also set a directory for downloader to install new cogs to, the default
|
This module allows you to load cogs from multiple directories and even from
|
||||||
being the :code:`cogs/` folder in the root bot directory.
|
outside the bot directory. You may also set a directory for downloader to
|
||||||
|
install new cogs to, the default being the :code:`cogs/` folder in the root
|
||||||
|
bot directory.
|
||||||
"""
|
"""
|
||||||
def __init__(self, paths: Tuple[str]=()):
|
def __init__(self, paths: Tuple[str]=()):
|
||||||
self.conf = Config.get_conf(self, 2938473984732, True)
|
self.conf = Config.get_conf(self, 2938473984732, True)
|
||||||
@ -33,8 +35,13 @@ class CogManager:
|
|||||||
self._paths = list(paths)
|
self._paths = list(paths)
|
||||||
|
|
||||||
async def paths(self) -> Tuple[Path, ...]:
|
async def paths(self) -> Tuple[Path, ...]:
|
||||||
"""
|
"""Get all currently valid path directories.
|
||||||
All currently valid path directories.
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`tuple` of `pathlib.Path`
|
||||||
|
All valid cog paths.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
conf_paths = await self.conf.paths()
|
conf_paths = await self.conf.paths()
|
||||||
other_paths = self._paths
|
other_paths = self._paths
|
||||||
@ -47,26 +54,40 @@ class CogManager:
|
|||||||
return tuple(p.resolve() for p in paths if p.is_dir())
|
return tuple(p.resolve() for p in paths if p.is_dir())
|
||||||
|
|
||||||
async def install_path(self) -> Path:
|
async def install_path(self) -> Path:
|
||||||
"""
|
"""Get the install path for 3rd party cogs.
|
||||||
The install path for 3rd party cogs.
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
pathlib.Path
|
||||||
|
The path to the directory where 3rd party cogs are stored.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
p = Path(await self.conf.install_path())
|
p = Path(await self.conf.install_path())
|
||||||
return p.resolve()
|
return p.resolve()
|
||||||
|
|
||||||
async def set_install_path(self, path: Path) -> Path:
|
async def set_install_path(self, path: Path) -> Path:
|
||||||
"""
|
"""Set the install path for 3rd party cogs.
|
||||||
Install path setter, will return the absolute path to
|
|
||||||
the given path.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
The bot will not remember your old cog install path which means
|
The bot will not remember your old cog install path which means
|
||||||
that ALL PREVIOUSLY INSTALLED COGS will now be unfindable.
|
that **all previously installed cogs** will no longer be found.
|
||||||
|
|
||||||
:param pathlib.Path path:
|
Parameters
|
||||||
|
----------
|
||||||
|
path : pathlib.Path
|
||||||
The new directory for cog installs.
|
The new directory for cog installs.
|
||||||
:raises ValueError:
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
pathlib.Path
|
||||||
|
Absolute path to the new install directory.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
If :code:`path` is not an existing directory.
|
If :code:`path` is not an existing directory.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not path.is_dir():
|
if not path.is_dir():
|
||||||
raise ValueError("The install path must be an existing directory.")
|
raise ValueError("The install path must be an existing directory.")
|
||||||
@ -76,14 +97,16 @@ class CogManager:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _ensure_path_obj(path: Union[Path, str]) -> Path:
|
def _ensure_path_obj(path: Union[Path, str]) -> Path:
|
||||||
"""
|
"""Guarantee an object will be a path object.
|
||||||
Guarantees an object will be a path object.
|
|
||||||
|
|
||||||
:param path:
|
Parameters
|
||||||
:type path:
|
----------
|
||||||
pathlib.Path or str
|
path : `pathlib.Path` or `str`
|
||||||
:rtype:
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
pathlib.Path
|
pathlib.Path
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
path.exists()
|
path.exists()
|
||||||
@ -92,17 +115,21 @@ class CogManager:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
async def add_path(self, path: Union[Path, str]):
|
async def add_path(self, path: Union[Path, str]):
|
||||||
"""
|
"""Add a cog path to current list.
|
||||||
Adds a cog path to current list, will ignore duplicates. Does have
|
|
||||||
a side effect of removing all invalid paths from the saved path
|
|
||||||
list.
|
|
||||||
|
|
||||||
:param path:
|
This will ignore duplicates. Does have a side effect of removing all
|
||||||
|
invalid paths from the saved path list.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
path : `pathlib.Path` or `str`
|
||||||
Path to add.
|
Path to add.
|
||||||
:type path:
|
|
||||||
pathlib.Path or str
|
Raises
|
||||||
:raises ValueError:
|
------
|
||||||
|
ValueError
|
||||||
If :code:`path` does not resolve to an existing directory.
|
If :code:`path` does not resolve to an existing directory.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
path = self._ensure_path_obj(path)
|
path = self._ensure_path_obj(path)
|
||||||
|
|
||||||
@ -121,15 +148,18 @@ class CogManager:
|
|||||||
await self.set_paths(all_paths)
|
await self.set_paths(all_paths)
|
||||||
|
|
||||||
async def remove_path(self, path: Union[Path, str]) -> Tuple[Path, ...]:
|
async def remove_path(self, path: Union[Path, str]) -> Tuple[Path, ...]:
|
||||||
"""
|
"""Remove a path from the current paths list.
|
||||||
Removes a path from the current paths list.
|
|
||||||
|
|
||||||
:param path: Path to remove.
|
Parameters
|
||||||
:type path:
|
----------
|
||||||
pathlib.Path or str
|
path : `pathlib.Path` or `str`
|
||||||
:return:
|
Path to remove.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`tuple` of `pathlib.Path`
|
||||||
Tuple of new valid paths.
|
Tuple of new valid paths.
|
||||||
:rtype: tuple
|
|
||||||
"""
|
"""
|
||||||
path = self._ensure_path_obj(path)
|
path = self._ensure_path_obj(path)
|
||||||
all_paths = list(await self.paths())
|
all_paths = list(await self.paths())
|
||||||
@ -139,27 +169,35 @@ class CogManager:
|
|||||||
return tuple(all_paths)
|
return tuple(all_paths)
|
||||||
|
|
||||||
async def set_paths(self, paths_: List[Path]):
|
async def set_paths(self, paths_: List[Path]):
|
||||||
"""
|
"""Set the current paths list.
|
||||||
Sets the current paths list.
|
|
||||||
|
|
||||||
:param List[pathlib.Path] paths_:
|
Parameters
|
||||||
|
----------
|
||||||
|
paths_ : `list` of `pathlib.Path`
|
||||||
List of paths to set.
|
List of paths to set.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
str_paths = [str(p) for p in paths_]
|
str_paths = [str(p) for p in paths_]
|
||||||
await self.conf.paths.set(str_paths)
|
await self.conf.paths.set(str_paths)
|
||||||
|
|
||||||
async def find_cog(self, name: str) -> ModuleSpec:
|
async def find_cog(self, name: str) -> ModuleSpec:
|
||||||
"""
|
"""Find a cog in the list of available paths.
|
||||||
Finds a cog in the list of available paths.
|
|
||||||
|
|
||||||
:param name:
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
Name of the cog to find.
|
Name of the cog to find.
|
||||||
:raises RuntimeError:
|
|
||||||
If there is no cog with the given name.
|
Returns
|
||||||
:return:
|
-------
|
||||||
A module spec to be used for specialized cog loading.
|
|
||||||
:rtype:
|
|
||||||
importlib.machinery.ModuleSpec
|
importlib.machinery.ModuleSpec
|
||||||
|
A module spec to be used for specialized cog loading.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If there is no cog with the given name.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
resolved_paths = [str(p.resolve()) for p in await self.paths()]
|
resolved_paths = [str(p.resolve()) for p in await self.paths()]
|
||||||
for finder, module_name, _ in pkgutil.iter_modules(resolved_paths):
|
for finder, module_name, _ in pkgutil.iter_modules(resolved_paths):
|
||||||
@ -173,11 +211,10 @@ class CogManager:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def invalidate_caches():
|
def invalidate_caches():
|
||||||
"""
|
"""Re-evaluate modules in the py cache.
|
||||||
|
|
||||||
This is an alias for an importlib internal and should be called
|
This is an alias for an importlib internal and should be called
|
||||||
any time that a new module has been installed to a cog directory.
|
any time that a new module has been installed to a cog directory.
|
||||||
|
|
||||||
*I think.*
|
|
||||||
"""
|
"""
|
||||||
invalidate_caches()
|
invalidate_caches()
|
||||||
|
|
||||||
|
|||||||
@ -11,20 +11,18 @@ log = logging.getLogger("red.config")
|
|||||||
|
|
||||||
|
|
||||||
class Value:
|
class Value:
|
||||||
"""
|
"""A singular "value" of data.
|
||||||
A singular "value" of data.
|
|
||||||
|
|
||||||
.. py:attribute:: identifiers
|
Attributes
|
||||||
|
----------
|
||||||
|
identifiers : `tuple` of `str`
|
||||||
|
This attribute provides all the keys necessary to get a specific data
|
||||||
|
element from a json document.
|
||||||
|
default
|
||||||
|
The default value for the data element that `identifiers` points at.
|
||||||
|
spawner : `redbot.core.drivers.red_base.BaseDriver`
|
||||||
|
A reference to `Config.spawner`.
|
||||||
|
|
||||||
This attribute provides all the keys necessary to get a specific data element from a json document.
|
|
||||||
|
|
||||||
.. py:attribute:: default
|
|
||||||
|
|
||||||
The default value for the data element that :py:attr:`identifiers` points at.
|
|
||||||
|
|
||||||
.. py:attribute:: spawner
|
|
||||||
|
|
||||||
A reference to :py:attr:`.Config.spawner`.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, identifiers: Tuple[str], default_value, spawner):
|
def __init__(self, identifiers: Tuple[str], default_value, spawner):
|
||||||
self._identifiers = identifiers
|
self._identifiers = identifiers
|
||||||
@ -45,12 +43,15 @@ class Value:
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def __call__(self, default=None):
|
def __call__(self, default=None):
|
||||||
"""
|
"""Get the literal value of this data element.
|
||||||
Each :py:class:`Value` object is created by the :py:meth:`Group.__getattr__` method.
|
|
||||||
The "real" data of the :py:class:`Value` object is accessed by this method. It is a replacement for a
|
|
||||||
:python:`get()` method.
|
|
||||||
|
|
||||||
For example::
|
Each `Value` object is created by the `Group.__getattr__` method. The
|
||||||
|
"real" data of the `Value` object is accessed by this method. It is a
|
||||||
|
replacement for a :code:`get()` method.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
::
|
||||||
|
|
||||||
foo = await conf.guild(some_guild).foo()
|
foo = await conf.guild(some_guild).foo()
|
||||||
|
|
||||||
@ -64,26 +65,39 @@ class Value:
|
|||||||
|
|
||||||
This is now, for all intents and purposes, a coroutine.
|
This is now, for all intents and purposes, a coroutine.
|
||||||
|
|
||||||
:param default:
|
Parameters
|
||||||
This argument acts as an override for the registered default provided by :py:attr:`default`. This argument
|
----------
|
||||||
is ignored if its value is :python:`None`.
|
default : `object`, optional
|
||||||
:type default: Optional[object]
|
This argument acts as an override for the registered default
|
||||||
:return:
|
provided by `default`. This argument is ignored if its
|
||||||
|
value is :code:`None`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
types.coroutine
|
||||||
A coroutine object that must be awaited.
|
A coroutine object that must be awaited.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self._get(default)
|
return self._get(default)
|
||||||
|
|
||||||
async def set(self, value):
|
async def set(self, value):
|
||||||
"""
|
"""Set the value of the data elements pointed to by `identifiers`.
|
||||||
Sets the value of the data element indicate by :py:attr:`identifiers`.
|
|
||||||
|
|
||||||
For example::
|
Example
|
||||||
|
-------
|
||||||
|
::
|
||||||
|
|
||||||
# Sets global value "foo" to False
|
# Sets global value "foo" to False
|
||||||
await conf.foo.set(False)
|
await conf.foo.set(False)
|
||||||
|
|
||||||
# Sets guild specific value of "bar" to True
|
# Sets guild specific value of "bar" to True
|
||||||
await conf.guild(some_guild).bar.set(True)
|
await conf.guild(some_guild).bar.set(True)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
value
|
||||||
|
The new literal value of this attribute.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
driver = self.spawner.get_driver()
|
driver = self.spawner.get_driver()
|
||||||
await driver.set(self.identifiers, value)
|
await driver.set(self.identifiers, value)
|
||||||
@ -91,16 +105,20 @@ class Value:
|
|||||||
|
|
||||||
class Group(Value):
|
class Group(Value):
|
||||||
"""
|
"""
|
||||||
A "group" of data, inherits from :py:class:`.Value` which means that all of the attributes and methods available
|
Represents a group of data, composed of more `Group` or `Value` objects.
|
||||||
in :py:class:`.Value` are also available when working with a :py:class:`.Group` object.
|
|
||||||
|
|
||||||
.. py:attribute:: defaults
|
Inherits from `Value` which means that all of the attributes and methods
|
||||||
|
available in `Value` are also available when working with a `Group` object.
|
||||||
|
|
||||||
A dictionary of registered default values for this :py:class:`Group`.
|
Attributes
|
||||||
|
----------
|
||||||
|
defaults : `dict`
|
||||||
|
All registered default values for this Group.
|
||||||
|
force_registration : `bool`
|
||||||
|
Same as `Config.force_registration`.
|
||||||
|
spawner : `redbot.core.drivers.red_base.BaseDriver`
|
||||||
|
A reference to `Config.spawner`.
|
||||||
|
|
||||||
.. py:attribute:: force_registration
|
|
||||||
|
|
||||||
See :py:attr:`.Config.force_registration`.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, identifiers: Tuple[str],
|
def __init__(self, identifiers: Tuple[str],
|
||||||
defaults: dict,
|
defaults: dict,
|
||||||
@ -118,18 +136,28 @@ class Group(Value):
|
|||||||
|
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
def __getattr__(self, item: str) -> Union["Group", Value]:
|
def __getattr__(self, item: str) -> Union["Group", Value]:
|
||||||
"""
|
"""Get an attribute of this group.
|
||||||
Takes in the next accessible item.
|
|
||||||
|
|
||||||
1. If it's found to be a group of data we return another :py:class:`Group` object.
|
This special method is called whenever dot notation is used on this
|
||||||
2. If it's found to be a data value we return a :py:class:`.Value` object.
|
object.
|
||||||
3. If it is not found and :py:attr:`force_registration` is :python:`True` then we raise
|
|
||||||
:py:exc:`AttributeError`.
|
Parameters
|
||||||
4. Otherwise return a :py:class:`.Value` object.
|
----------
|
||||||
|
item : str
|
||||||
|
The name of the attribute being accessed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`Group` or `Value`
|
||||||
|
A child value of this Group. This, of course, can be another
|
||||||
|
`Group`, due to Config's composite pattern.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
AttributeError
|
||||||
|
If the attribute has not been registered and `force_registration`
|
||||||
|
is set to :code:`True`.
|
||||||
|
|
||||||
:param str item:
|
|
||||||
The name of the item a cog is attempting to access through normal Python attribute
|
|
||||||
access.
|
|
||||||
"""
|
"""
|
||||||
is_group = self.is_group(item)
|
is_group = self.is_group(item)
|
||||||
is_value = not is_group and self.is_value(item)
|
is_value = not is_group and self.is_value(item)
|
||||||
@ -170,21 +198,27 @@ class Group(Value):
|
|||||||
return super_group
|
return super_group
|
||||||
|
|
||||||
def is_group(self, item: str) -> bool:
|
def is_group(self, item: str) -> bool:
|
||||||
"""
|
"""A helper method for `__getattr__`. Most developers will have no need
|
||||||
A helper method for :py:meth:`__getattr__`. Most developers will have no need to use this.
|
to use this.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
item : str
|
||||||
|
See `__getattr__`.
|
||||||
|
|
||||||
:param str item:
|
|
||||||
See :py:meth:`__getattr__`.
|
|
||||||
"""
|
"""
|
||||||
default = self._defaults.get(item)
|
default = self._defaults.get(item)
|
||||||
return isinstance(default, dict)
|
return isinstance(default, dict)
|
||||||
|
|
||||||
def is_value(self, item: str) -> bool:
|
def is_value(self, item: str) -> bool:
|
||||||
"""
|
"""A helper method for `__getattr__`. Most developers will have no need
|
||||||
A helper method for :py:meth:`__getattr__`. Most developers will have no need to use this.
|
to use this.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
item : str
|
||||||
|
See `__getattr__`.
|
||||||
|
|
||||||
:param str item:
|
|
||||||
See :py:meth:`__getattr__`.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
default = self._defaults[item]
|
default = self._defaults[item]
|
||||||
@ -194,14 +228,18 @@ class Group(Value):
|
|||||||
return not isinstance(default, dict)
|
return not isinstance(default, dict)
|
||||||
|
|
||||||
def get_attr(self, item: str, default=None, resolve=True):
|
def get_attr(self, item: str, default=None, resolve=True):
|
||||||
"""
|
"""Manually get an attribute of this Group.
|
||||||
This is available to use as an alternative to using normal Python attribute access. It is required if you find
|
|
||||||
a need for dynamic attribute access.
|
|
||||||
|
|
||||||
.. note::
|
This is available to use as an alternative to using normal Python
|
||||||
|
attribute access. It is required if you find a need for dynamic
|
||||||
|
attribute access.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
Use of this method should be avoided wherever possible.
|
Use of this method should be avoided wherever possible.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
A possible use case::
|
A possible use case::
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@ -211,16 +249,24 @@ class Group(Value):
|
|||||||
# Where the value of item is the name of the data field in Config
|
# Where the value of item is the name of the data field in Config
|
||||||
await ctx.send(await self.conf.user(user).get_attr(item))
|
await ctx.send(await self.conf.user(user).get_attr(item))
|
||||||
|
|
||||||
:param str item:
|
Parameters
|
||||||
The name of the data field in :py:class:`.Config`.
|
----------
|
||||||
:param default:
|
item : str
|
||||||
This is an optional override to the registered default for this item.
|
The name of the data field in `Config`.
|
||||||
:param resolve:
|
default
|
||||||
If this is :code:`True` this function will return a coroutine that resolves to a "real" data value,
|
This is an optional override to the registered default for this
|
||||||
if :code:`False` this function will return an instance of :py:class:`Group` or :py:class:`Value`
|
item.
|
||||||
depending on the type of the "real" data value.
|
resolve : bool
|
||||||
:rtype:
|
If this is :code:`True` this function will return a coroutine that
|
||||||
Coroutine or Value
|
resolves to a "real" data value when awaited. If :code:`False`,
|
||||||
|
this method acts the same as `__getattr__`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`types.coroutine` or `Value` or `Group`
|
||||||
|
The attribute which was requested, its type depending on the value
|
||||||
|
of :code:`resolve`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
value = getattr(self, item)
|
value = getattr(self, item)
|
||||||
if resolve:
|
if resolve:
|
||||||
@ -229,36 +275,42 @@ class Group(Value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
async def all(self) -> dict:
|
async def all(self) -> dict:
|
||||||
"""
|
"""Get a dictionary representation of this group's data.
|
||||||
This method allows you to get "all" of a particular group of data. It will return the dictionary of all data
|
|
||||||
for a particular Guild/Channel/Role/User/Member etc.
|
|
||||||
|
|
||||||
.. note::
|
Note
|
||||||
|
----
|
||||||
|
The return value of this method will include registered defaults for
|
||||||
|
values which have not yet been set.
|
||||||
|
|
||||||
Any values that have not been set from the registered defaults will have their default values
|
Returns
|
||||||
added to the dictionary that this method returns.
|
-------
|
||||||
|
dict
|
||||||
|
All of this Group's attributes, resolved as raw data values.
|
||||||
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
"""
|
||||||
defaults = self.defaults
|
defaults = self.defaults
|
||||||
defaults.update(await self())
|
defaults.update(await self())
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
async def all_from_kind(self) -> dict:
|
async def all_from_kind(self) -> dict:
|
||||||
"""
|
"""Get all data from this group and its siblings.
|
||||||
This method allows you to get all data from all entries in a given Kind. It will return a dictionary of Kind
|
|
||||||
ID's -> data.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Any values that have not been set from the registered defaults will have their default values
|
|
||||||
added to the dictionary that this method returns.
|
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
|
|
||||||
This method is overridden in :py:meth:`.MemberGroup.all_from_kind` and functions slightly differently.
|
This method is overridden in `MemberGroup.all_from_kind` and
|
||||||
|
functions slightly differently.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
|
The return value of this method will include registered defaults
|
||||||
|
for groups which have not had their values set.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
A dict of :code:`ID -> data`, with the data being a dict
|
||||||
|
of the group's raw values.
|
||||||
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
all_from_kind = await self._super_group()
|
all_from_kind = await self._super_group()
|
||||||
@ -278,35 +330,47 @@ class Group(Value):
|
|||||||
await super().set(value)
|
await super().set(value)
|
||||||
|
|
||||||
async def set_attr(self, item: str, value):
|
async def set_attr(self, item: str, value):
|
||||||
"""
|
"""Set an attribute by its name.
|
||||||
Please see :py:meth:`get_attr` for more information.
|
|
||||||
|
|
||||||
.. note::
|
Similar to `get_attr` in the way it can be used to dynamically set
|
||||||
|
attributes by name.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
Use of this method should be avoided wherever possible.
|
Use of this method should be avoided wherever possible.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
item : str
|
||||||
|
The name of the attribute being set.
|
||||||
|
value
|
||||||
|
The raw data value to set the attribute as.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
value_obj = getattr(self, item)
|
value_obj = getattr(self, item)
|
||||||
await value_obj.set(value)
|
await value_obj.set(value)
|
||||||
|
|
||||||
async def clear(self):
|
async def clear(self):
|
||||||
"""
|
"""Wipe all data from this group.
|
||||||
Wipes all data from the given Guild/Channel/Role/Member/User. If used on a global group, it will wipe all global
|
|
||||||
data.
|
If used on a global group, it will wipe all global data, but not
|
||||||
|
local data.
|
||||||
"""
|
"""
|
||||||
await self.set({})
|
await self.set({})
|
||||||
|
|
||||||
async def clear_all(self):
|
async def clear_all(self):
|
||||||
"""
|
"""Wipe all data from this group and its siblings.
|
||||||
Wipes all data from all Guilds/Channels/Roles/Members/Users. If used on a global group, this method wipes all
|
|
||||||
data from everything.
|
If used on a global group, this method wipes all data from all groups.
|
||||||
"""
|
"""
|
||||||
await self._super_group.set({})
|
await self._super_group.set({})
|
||||||
|
|
||||||
|
|
||||||
class MemberGroup(Group):
|
class MemberGroup(Group):
|
||||||
"""
|
"""A specific group class for use with member data only.
|
||||||
A specific group class for use with member data only. Inherits from :py:class:`.Group`. In this group data is
|
|
||||||
stored as :code:`GUILD_ID -> MEMBER_ID -> data`.
|
Inherits from `Group`. In this group data is stored as
|
||||||
|
:code:`GUILD_ID -> MEMBER_ID -> data`.
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def _super_group(self) -> Group:
|
def _super_group(self) -> Group:
|
||||||
@ -329,29 +393,35 @@ class MemberGroup(Group):
|
|||||||
return group_obj
|
return group_obj
|
||||||
|
|
||||||
async def all_guilds(self) -> dict:
|
async def all_guilds(self) -> dict:
|
||||||
"""
|
"""Get a dict of :code:`GUILD_ID -> MEMBER_ID -> data`.
|
||||||
Returns a dict of :code:`GUILD_ID -> MEMBER_ID -> data`.
|
|
||||||
|
|
||||||
.. note::
|
Note
|
||||||
|
----
|
||||||
|
The return value of this method will include registered defaults
|
||||||
|
for groups which have not had their values set.
|
||||||
|
|
||||||
Any values that have not been set from the registered defaults will have their default values
|
Returns
|
||||||
added to the dictionary that this method returns.
|
-------
|
||||||
|
dict
|
||||||
|
A dict of data from all members from all guilds.
|
||||||
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
return await super().all_from_kind()
|
return await super().all_from_kind()
|
||||||
|
|
||||||
async def all_from_kind(self) -> dict:
|
async def all_from_kind(self) -> dict:
|
||||||
"""
|
"""Get a dict of all members from the same guild as the given one.
|
||||||
Returns a dict of all members from the same guild as the given one.
|
|
||||||
|
|
||||||
.. note::
|
Note
|
||||||
|
----
|
||||||
|
The return value of this method will include registered defaults
|
||||||
|
for groups which have not had their values set.
|
||||||
|
|
||||||
Any values that have not been set from the registered defaults will have their default values
|
Returns
|
||||||
added to the dictionary that this method returns.
|
-------
|
||||||
|
dict
|
||||||
|
A dict of :code:`MEMBER_ID -> data`.
|
||||||
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
"""
|
||||||
guild_member = await super().all_from_kind()
|
guild_member = await super().all_from_kind()
|
||||||
return guild_member.get(self.identifiers[-2], {})
|
return guild_member.get(self.identifiers[-2], {})
|
||||||
@ -359,8 +429,10 @@ class MemberGroup(Group):
|
|||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""
|
"""Configuration manager for cogs and Red.
|
||||||
You should always use :func:`get_conf` or :func:`get_core_conf` to initialize a Config object.
|
|
||||||
|
You should always use `get_conf` or to instantiate a Config object. Use
|
||||||
|
`get_core_conf` for Config used in the core package.
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
Most config data should be accessed through its respective group method (e.g. :py:meth:`guild`)
|
Most config data should be accessed through its respective group method (e.g. :py:meth:`guild`)
|
||||||
@ -369,27 +441,26 @@ class Config:
|
|||||||
|
|
||||||
await conf.foo()
|
await conf.foo()
|
||||||
|
|
||||||
.. py:attribute:: cog_name
|
Attributes
|
||||||
|
----------
|
||||||
|
cog_name : `str`
|
||||||
|
The name of the cog that has requested a `Config` object.
|
||||||
|
unique_identifier : `int`
|
||||||
|
Unique identifier provided to differentiate cog data when name
|
||||||
|
conflicts occur.
|
||||||
|
spawner
|
||||||
|
A callable object that returns some driver that implements
|
||||||
|
`redbot.core.drivers.red_base.BaseDriver`.
|
||||||
|
force_registration : `bool`
|
||||||
|
Determines if Config should throw an error if a cog attempts to access
|
||||||
|
an attribute which has not been previously registered.
|
||||||
|
|
||||||
The name of the cog that has requested a :py:class:`.Config` object.
|
Note
|
||||||
|
----
|
||||||
|
**You should use this.** By enabling force registration you give Config
|
||||||
|
the ability to alert you instantly if you've made a typo when
|
||||||
|
attempting to access data.
|
||||||
|
|
||||||
.. py:attribute:: unique_identifier
|
|
||||||
|
|
||||||
Unique identifier provided to differentiate cog data when name conflicts occur.
|
|
||||||
|
|
||||||
.. py:attribute:: spawner
|
|
||||||
|
|
||||||
A callable object that returns some driver that implements :py:class:`.drivers.red_base.BaseDriver`.
|
|
||||||
|
|
||||||
.. py:attribute:: force_registration
|
|
||||||
|
|
||||||
A boolean that determines if :py:class:`.Config` should throw an error if a cog attempts to access an attribute
|
|
||||||
which has not been previously registered.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**You should use this.** By enabling force registration you give :py:class:`.Config` the ability to alert
|
|
||||||
you instantly if you've made a typo when attempting to access data.
|
|
||||||
"""
|
"""
|
||||||
GLOBAL = "GLOBAL"
|
GLOBAL = "GLOBAL"
|
||||||
GUILD = "GUILD"
|
GUILD = "GUILD"
|
||||||
@ -416,19 +487,26 @@ class Config:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_conf(cls, cog_instance, identifier: int,
|
def get_conf(cls, cog_instance, identifier: int,
|
||||||
force_registration=False):
|
force_registration=False):
|
||||||
"""
|
"""Get a Config instance for your cog.
|
||||||
Returns a Config instance based on a simplified set of initial
|
|
||||||
variables.
|
Parameters
|
||||||
|
----------
|
||||||
|
cog_instance
|
||||||
|
This is an instance of your cog after it has been instantiated. If
|
||||||
|
you're calling this method from within your cog's :code:`__init__`,
|
||||||
|
this is just :code:`self`.
|
||||||
|
identifier : int
|
||||||
|
A (hard-coded) random integer, used to keep your data distinct from
|
||||||
|
any other cog with the same name.
|
||||||
|
force_registration : `bool`, optional
|
||||||
|
Should config require registration of data keys before allowing you
|
||||||
|
to get/set values? See `force_registration`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Config
|
||||||
|
A new Config object.
|
||||||
|
|
||||||
:param cog_instance:
|
|
||||||
:param identifier:
|
|
||||||
Any random integer, used to keep your data
|
|
||||||
distinct from any other cog with the same name.
|
|
||||||
:param force_registration:
|
|
||||||
Should config require registration
|
|
||||||
of data keys before allowing you to get/set values?
|
|
||||||
:return:
|
|
||||||
A new config object.
|
|
||||||
"""
|
"""
|
||||||
cog_path_override = cog_data_path(cog_instance)
|
cog_path_override = cog_data_path(cog_instance)
|
||||||
cog_name = cog_path_override.stem
|
cog_name = cog_path_override.stem
|
||||||
@ -441,15 +519,14 @@ class Config:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_core_conf(cls, force_registration: bool=False):
|
def get_core_conf(cls, force_registration: bool=False):
|
||||||
"""
|
"""All core modules that require a config instance should use this
|
||||||
All core modules that require a config instance should use this classmethod instead of
|
classmethod instead of `get_conf`.
|
||||||
:py:meth:`get_conf`
|
|
||||||
|
identifier : int
|
||||||
|
See `get_conf`.
|
||||||
|
force_registration : `bool`, optional
|
||||||
|
See `force_registration`.
|
||||||
|
|
||||||
:param int identifier:
|
|
||||||
See :py:meth:`get_conf`
|
|
||||||
:param force_registration:
|
|
||||||
See :py:attr:`force_registration`
|
|
||||||
:type force_registration: Optional[bool]
|
|
||||||
"""
|
"""
|
||||||
driver_spawn = JSONDriver("Core", data_path_override=core_data_path())
|
driver_spawn = JSONDriver("Core", data_path_override=core_data_path())
|
||||||
return cls(cog_name="Core", driver_spawn=driver_spawn,
|
return cls(cog_name="Core", driver_spawn=driver_spawn,
|
||||||
@ -457,11 +534,23 @@ class Config:
|
|||||||
force_registration=force_registration)
|
force_registration=force_registration)
|
||||||
|
|
||||||
def __getattr__(self, item: str) -> Union[Group, Value]:
|
def __getattr__(self, item: str) -> Union[Group, Value]:
|
||||||
"""
|
"""Same as `group.__getattr__` except for global data.
|
||||||
This is used to generate Value or Group objects for global
|
|
||||||
values.
|
Parameters
|
||||||
:param item:
|
----------
|
||||||
:return:
|
item : str
|
||||||
|
The attribute you want to get.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`Group` or `Value`
|
||||||
|
The value for the attribute you want to retrieve
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
AttributeError
|
||||||
|
If there is no global attribute by the given name and
|
||||||
|
`force_registration` is set to :code:`True`.
|
||||||
"""
|
"""
|
||||||
global_group = self._get_base_group(self.GLOBAL)
|
global_group = self._get_base_group(self.GLOBAL)
|
||||||
return getattr(global_group, item)
|
return getattr(global_group, item)
|
||||||
@ -526,9 +615,11 @@ class Config:
|
|||||||
self._update_defaults(to_add, self._defaults[key])
|
self._update_defaults(to_add, self._defaults[key])
|
||||||
|
|
||||||
def register_global(self, **kwargs):
|
def register_global(self, **kwargs):
|
||||||
"""
|
"""Register default values for attributes you wish to store in `Config`
|
||||||
Registers default values for attributes you wish to store in :py:class:`.Config` at a global level.
|
at a global level.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
You can register a single value or multiple values::
|
You can register a single value or multiple values::
|
||||||
|
|
||||||
conf.register_global(
|
conf.register_global(
|
||||||
@ -562,37 +653,47 @@ class Config:
|
|||||||
foo__bar=True,
|
foo__bar=True,
|
||||||
foo__baz=False
|
foo__baz=False
|
||||||
)
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._register_default(self.GLOBAL, **kwargs)
|
self._register_default(self.GLOBAL, **kwargs)
|
||||||
|
|
||||||
def register_guild(self, **kwargs):
|
def register_guild(self, **kwargs):
|
||||||
"""
|
"""Register default values on a per-guild level.
|
||||||
Registers default values on a per-guild level. See :py:meth:`register_global` for more details.
|
|
||||||
|
See :py:meth:`register_global` for more details.
|
||||||
"""
|
"""
|
||||||
self._register_default(self.GUILD, **kwargs)
|
self._register_default(self.GUILD, **kwargs)
|
||||||
|
|
||||||
def register_channel(self, **kwargs):
|
def register_channel(self, **kwargs):
|
||||||
"""
|
"""Register default values on a per-channel level.
|
||||||
Registers default values on a per-channel level. See :py:meth:`register_global` for more details.
|
|
||||||
|
See `register_global` for more details.
|
||||||
"""
|
"""
|
||||||
# We may need to add a voice channel category later
|
# We may need to add a voice channel category later
|
||||||
self._register_default(self.CHANNEL, **kwargs)
|
self._register_default(self.CHANNEL, **kwargs)
|
||||||
|
|
||||||
def register_role(self, **kwargs):
|
def register_role(self, **kwargs):
|
||||||
"""
|
"""Registers default values on a per-role level.
|
||||||
Registers default values on a per-role level. See :py:meth:`register_global` for more details.
|
|
||||||
|
See `register_global` for more details.
|
||||||
"""
|
"""
|
||||||
self._register_default(self.ROLE, **kwargs)
|
self._register_default(self.ROLE, **kwargs)
|
||||||
|
|
||||||
def register_user(self, **kwargs):
|
def register_user(self, **kwargs):
|
||||||
"""
|
"""Registers default values on a per-user level.
|
||||||
Registers default values on a per-user level (i.e. server-independent). See :py:meth:`register_global` for more details.
|
|
||||||
|
This means that each user's data is guild-independent.
|
||||||
|
|
||||||
|
See `register_global` for more details.
|
||||||
"""
|
"""
|
||||||
self._register_default(self.USER, **kwargs)
|
self._register_default(self.USER, **kwargs)
|
||||||
|
|
||||||
def register_member(self, **kwargs):
|
def register_member(self, **kwargs):
|
||||||
"""
|
"""Registers default values on a per-member level.
|
||||||
Registers default values on a per-member level (i.e. server-dependent). See :py:meth:`register_global` for more details.
|
|
||||||
|
This means that each user's data is guild-dependent.
|
||||||
|
|
||||||
|
See `register_global` for more details.
|
||||||
"""
|
"""
|
||||||
self._register_default(self.MEMBER, **kwargs)
|
self._register_default(self.MEMBER, **kwargs)
|
||||||
|
|
||||||
@ -607,43 +708,59 @@ class Config:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def guild(self, guild: discord.Guild) -> Group:
|
def guild(self, guild: discord.Guild) -> Group:
|
||||||
"""
|
"""Returns a `Group` for the given guild.
|
||||||
Returns a :py:class:`.Group` for the given guild.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
guild : discord.Guild
|
||||||
|
A guild object.
|
||||||
|
|
||||||
:param discord.Guild guild: A discord.py guild object.
|
|
||||||
"""
|
"""
|
||||||
return self._get_base_group(self.GUILD, guild.id)
|
return self._get_base_group(self.GUILD, guild.id)
|
||||||
|
|
||||||
def channel(self, channel: discord.TextChannel) -> Group:
|
def channel(self, channel: discord.TextChannel) -> Group:
|
||||||
"""
|
"""Returns a `Group` for the given channel.
|
||||||
Returns a :py:class:`.Group` for the given channel. This does not currently support differences between
|
|
||||||
text and voice channels.
|
This does not discriminate between text and voice channels.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
channel : `discord.abc.GuildChannel`
|
||||||
|
A channel object.
|
||||||
|
|
||||||
:param discord.TextChannel channel: A discord.py text channel object.
|
|
||||||
"""
|
"""
|
||||||
return self._get_base_group(self.CHANNEL, channel.id)
|
return self._get_base_group(self.CHANNEL, channel.id)
|
||||||
|
|
||||||
def role(self, role: discord.Role) -> Group:
|
def role(self, role: discord.Role) -> Group:
|
||||||
"""
|
"""Returns a `Group` for the given role.
|
||||||
Returns a :py:class:`.Group` for the given role.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
role : discord.Role
|
||||||
|
A role object.
|
||||||
|
|
||||||
:param discord.Role role: A discord.py role object.
|
|
||||||
"""
|
"""
|
||||||
return self._get_base_group(self.ROLE, role.id)
|
return self._get_base_group(self.ROLE, role.id)
|
||||||
|
|
||||||
def user(self, user: discord.User) -> Group:
|
def user(self, user: discord.User) -> Group:
|
||||||
"""
|
"""Returns a `Group` for the given user.
|
||||||
Returns a :py:class:`.Group` for the given user.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
user : discord.User
|
||||||
|
A user object.
|
||||||
|
|
||||||
:param discord.User user: A discord.py user object.
|
|
||||||
"""
|
"""
|
||||||
return self._get_base_group(self.USER, user.id)
|
return self._get_base_group(self.USER, user.id)
|
||||||
|
|
||||||
def member(self, member: discord.Member) -> MemberGroup:
|
def member(self, member: discord.Member) -> MemberGroup:
|
||||||
"""
|
"""Returns a `Group` for the given member.
|
||||||
Returns a :py:class:`.MemberGroup` for the given member.
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
member : discord.Member
|
||||||
|
A member object.
|
||||||
|
|
||||||
:param discord.Member member: A discord.py member object.
|
|
||||||
"""
|
"""
|
||||||
return self._get_base_group(self.MEMBER, member.guild.id, member.id,
|
return self._get_base_group(self.MEMBER, member.guild.id, member.id,
|
||||||
group_class=MemberGroup)
|
group_class=MemberGroup)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
"""The purpose of this module is to allow for Red to
|
"""Module for Red's Context class
|
||||||
further customise the command invocation context provided
|
|
||||||
by discord.py.
|
The purpose of this module is to allow for Red to further customise the command
|
||||||
|
invocation context provided by discord.py.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@ -12,27 +13,37 @@ TICK = "\N{WHITE HEAVY CHECK MARK}"
|
|||||||
|
|
||||||
|
|
||||||
class RedContext(commands.Context):
|
class RedContext(commands.Context):
|
||||||
"""
|
"""Command invocation context for Red.
|
||||||
Command invocation context for Red.
|
|
||||||
|
|
||||||
All context passed into commands will be of this type.
|
All context passed into commands will be of this type.
|
||||||
|
|
||||||
This class inherits from
|
This class inherits from `commands.Context <discord.ext.commands.Context>`.
|
||||||
:py:class:`commands.Context <discord.ext.commands.Context>`.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def send_help(self):
|
async def send_help(self):
|
||||||
"""Send the command help message."""
|
"""Send the command help message.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
`list` of `discord.Message`
|
||||||
|
A list of help messages which were sent to the user.
|
||||||
|
|
||||||
|
"""
|
||||||
command = self.invoked_subcommand or self.command
|
command = self.invoked_subcommand or self.command
|
||||||
pages = await self.bot.formatter.format_help_for(self, command)
|
pages = await self.bot.formatter.format_help_for(self, command)
|
||||||
|
ret = []
|
||||||
for page in pages:
|
for page in pages:
|
||||||
await self.send(page)
|
ret.append(await self.send(page))
|
||||||
|
return ret
|
||||||
|
|
||||||
async def tick(self):
|
async def tick(self):
|
||||||
"""Add a tick reaction to the command message.
|
"""Add a tick reaction to the command message.
|
||||||
|
|
||||||
:return: ``True`` if adding the reaction succeeded.
|
Returns
|
||||||
:rtype: bool
|
-------
|
||||||
|
bool
|
||||||
|
:code:`True` if adding the reaction succeeded.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await self.message.add_reaction(TICK)
|
await self.message.add_reaction(TICK)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
|
__all__ = ["BaseDriver"]
|
||||||
|
|
||||||
class BaseDriver:
|
class BaseDriver:
|
||||||
def get_driver(self):
|
def get_driver(self):
|
||||||
|
|||||||
@ -5,6 +5,8 @@ from ..json_io import JsonIO
|
|||||||
|
|
||||||
from .red_base import BaseDriver
|
from .red_base import BaseDriver
|
||||||
|
|
||||||
|
__all__ = ["JSON"]
|
||||||
|
|
||||||
|
|
||||||
class JSON(BaseDriver):
|
class JSON(BaseDriver):
|
||||||
def __init__(self, cog_name, *, data_path_override: Path=None,
|
def __init__(self, cog_name, *, data_path_override: Path=None,
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import pymongo as m
|
|||||||
from .red_base import BaseDriver
|
from .red_base import BaseDriver
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Mongo", "RedMongoException", "MultipleMatches",
|
||||||
|
"MissingCollection"]
|
||||||
|
|
||||||
|
|
||||||
class RedMongoException(Exception):
|
class RedMongoException(Exception):
|
||||||
"""Base Red Mongo Exception class"""
|
"""Base Red Mongo Exception class"""
|
||||||
pass
|
pass
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user