mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
[V3 Docs] Docs for utils package (#1085)
* Docstrings for chat formatting * Docstrings for mod utils * Type checking * Override CSS to highlight object name in definition * More typing * Utils docs pages * Fix typo here
This commit is contained in:
parent
b141729830
commit
5cef3e13e1
11
docs/_static/theme_overrides.css
vendored
Normal file
11
docs/_static/theme_overrides.css
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* This overrides the style for the path to each object definition, whilst
|
||||||
|
* the name of the object remains unchanged.
|
||||||
|
*
|
||||||
|
* e.g. in the definition `redbot.core.Config`, `redbot.core` is the
|
||||||
|
* desclassname.
|
||||||
|
*/
|
||||||
|
code.descclassname {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
@ -103,6 +103,9 @@ html_theme = 'sphinx_rtd_theme'
|
|||||||
# html_theme_options = {}
|
# html_theme_options = {}
|
||||||
|
|
||||||
html_context = {
|
html_context = {
|
||||||
|
'css_files': [
|
||||||
|
'_static/theme_overrides.css'
|
||||||
|
],
|
||||||
# Enable the "Edit in GitHub link within the header of each page.
|
# Enable the "Edit in GitHub link within the header of each page.
|
||||||
'display_github': True,
|
'display_github': True,
|
||||||
'github_user': 'Cog-Creators',
|
'github_user': 'Cog-Creators',
|
||||||
|
|||||||
17
docs/framework_utils.rst
Normal file
17
docs/framework_utils.rst
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.. red's core utils documentation
|
||||||
|
|
||||||
|
=================
|
||||||
|
Utility Functions
|
||||||
|
=================
|
||||||
|
|
||||||
|
Chat Formatting
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. automodule:: redbot.core.utils.chat_formatting
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Mod Helpers
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. automodule:: redbot.core.utils.mod
|
||||||
|
:members:
|
||||||
@ -37,6 +37,7 @@ Welcome to Red - Discord Bot's documentation!
|
|||||||
framework_i18n
|
framework_i18n
|
||||||
framework_modlog
|
framework_modlog
|
||||||
framework_context
|
framework_context
|
||||||
|
framework_utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,39 +1,145 @@
|
|||||||
import itertools
|
import itertools
|
||||||
|
from typing import List, Iterator
|
||||||
|
|
||||||
def error(text):
|
def error(text: str) -> str:
|
||||||
|
"""Get text prefixed with an error emoji.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The new message.
|
||||||
|
|
||||||
|
"""
|
||||||
return "\N{NO ENTRY SIGN} {}".format(text)
|
return "\N{NO ENTRY SIGN} {}".format(text)
|
||||||
|
|
||||||
|
|
||||||
def warning(text):
|
def warning(text: str) -> str:
|
||||||
|
"""Get text prefixed with a warning emoji.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The new message.
|
||||||
|
|
||||||
|
"""
|
||||||
return "\N{WARNING SIGN} {}".format(text)
|
return "\N{WARNING SIGN} {}".format(text)
|
||||||
|
|
||||||
|
|
||||||
def info(text):
|
def info(text: str) -> str:
|
||||||
|
"""Get text prefixed with an info emoji.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The new message.
|
||||||
|
|
||||||
|
"""
|
||||||
return "\N{INFORMATION SOURCE} {}".format(text)
|
return "\N{INFORMATION SOURCE} {}".format(text)
|
||||||
|
|
||||||
|
|
||||||
def question(text):
|
def question(text: str) -> str:
|
||||||
|
"""Get text prefixed with a question emoji.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The new message.
|
||||||
|
|
||||||
|
"""
|
||||||
return "\N{BLACK QUESTION MARK ORNAMENT} {}".format(text)
|
return "\N{BLACK QUESTION MARK ORNAMENT} {}".format(text)
|
||||||
|
|
||||||
|
|
||||||
def bold(text):
|
def bold(text: str) -> str:
|
||||||
|
"""Get the given text in bold.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be marked up.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The marked up text.
|
||||||
|
|
||||||
|
"""
|
||||||
return "**{}**".format(text)
|
return "**{}**".format(text)
|
||||||
|
|
||||||
|
|
||||||
def box(text, lang=""):
|
def box(text: str, lang: str="") -> str:
|
||||||
|
"""Get the given text in a code block.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be marked up.
|
||||||
|
lang : `str`, optional
|
||||||
|
The syntax highlighting language for the codeblock.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The marked up text.
|
||||||
|
|
||||||
|
"""
|
||||||
ret = "```{}\n{}\n```".format(lang, text)
|
ret = "```{}\n{}\n```".format(lang, text)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def inline(text):
|
def inline(text: str) -> str:
|
||||||
|
"""Get the given text as inline code.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be marked up.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The marked up text.
|
||||||
|
|
||||||
|
"""
|
||||||
return "`{}`".format(text)
|
return "`{}`".format(text)
|
||||||
|
|
||||||
|
|
||||||
def italics(text):
|
def italics(text: str) -> str:
|
||||||
|
"""Get the given text in italics.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be marked up.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The marked up text.
|
||||||
|
|
||||||
|
"""
|
||||||
return "*{}*".format(text)
|
return "*{}*".format(text)
|
||||||
|
|
||||||
|
|
||||||
def bordered(text1: list, text2: list):
|
def bordered(text1: List[str], text2: List[str]) -> str:
|
||||||
|
"""Get two blocks of text in a borders.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
|
This will only work with a monospaced font.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text1 : `list` of `str`
|
||||||
|
The 1st block of text, with each string being a new line.
|
||||||
|
text2 : `list` of `str`
|
||||||
|
The 2nd block of text. Should not be longer than ``text1``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The bordered text.
|
||||||
|
|
||||||
|
"""
|
||||||
width1, width2 = max((len(s1) + 9, len(s2) + 9) for s1 in text1 for s2 in text2)
|
width1, width2 = max((len(s1) + 9, len(s2) + 9) for s1 in text1 for s2 in text2)
|
||||||
res = ['┌{}┐{}┌{}┐'.format("─"*width1, " "*4, "─"*width2)]
|
res = ['┌{}┐{}┌{}┐'.format("─"*width1, " "*4, "─"*width2)]
|
||||||
flag = True
|
flag = True
|
||||||
@ -50,9 +156,48 @@ def bordered(text1: list, text2: list):
|
|||||||
return "\n".join(res)
|
return "\n".join(res)
|
||||||
|
|
||||||
|
|
||||||
def pagify(text, delims=["\n"], *, priority=False, escape_mass_mentions=True, shorten_by=8,
|
def pagify(text: str,
|
||||||
page_length=2000):
|
delims: List[str]=["\n"],
|
||||||
"""DOES NOT RESPECT MARKDOWN BOXES OR INLINE CODE"""
|
*,
|
||||||
|
priority: bool=False,
|
||||||
|
escape_mass_mentions: bool=True,
|
||||||
|
shorten_by: int=8,
|
||||||
|
page_length: int=2000) -> Iterator[str]:
|
||||||
|
"""Generate multiple pages from the given text.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
|
This does not respect code blocks or inline code.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The content to pagify and send.
|
||||||
|
delims : `list` of `str`, optional
|
||||||
|
Characters where page breaks will occur. If no delimiters are found
|
||||||
|
in a page, the page will break after ``page_length`` characters.
|
||||||
|
By default this only contains the newline.
|
||||||
|
|
||||||
|
Other Parameters
|
||||||
|
----------------
|
||||||
|
priority : `bool`
|
||||||
|
Set to :code:`True` to choose the page break delimiter based on the
|
||||||
|
order of ``delims``. Otherwise, the page will always break at the
|
||||||
|
last possible delimiter.
|
||||||
|
escape_mass_mentions : `bool`
|
||||||
|
If :code:`True`, any mass mentions (here or everyone) will be
|
||||||
|
silenced.
|
||||||
|
shorten_by : `int`
|
||||||
|
How much to shorten each page by. Defaults to 8.
|
||||||
|
page_length : `int`
|
||||||
|
The maximum length of each page. Defaults to 2000.
|
||||||
|
|
||||||
|
Yields
|
||||||
|
------
|
||||||
|
`str`
|
||||||
|
Pages of the given text.
|
||||||
|
|
||||||
|
"""
|
||||||
in_text = text
|
in_text = text
|
||||||
page_length -= shorten_by
|
page_length -= shorten_by
|
||||||
while len(in_text) > page_length:
|
while len(in_text) > page_length:
|
||||||
@ -82,15 +227,59 @@ def pagify(text, delims=["\n"], *, priority=False, escape_mass_mentions=True, sh
|
|||||||
yield in_text
|
yield in_text
|
||||||
|
|
||||||
|
|
||||||
def strikethrough(text):
|
def strikethrough(text: str) -> str:
|
||||||
|
"""Get the given text with a strikethrough.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be marked up.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The marked up text.
|
||||||
|
|
||||||
|
"""
|
||||||
return "~~{}~~".format(text)
|
return "~~{}~~".format(text)
|
||||||
|
|
||||||
|
|
||||||
def underline(text):
|
def underline(text: str) -> str:
|
||||||
|
"""Get the given text with an underline.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be marked up.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The marked up text.
|
||||||
|
|
||||||
|
"""
|
||||||
return "__{}__".format(text)
|
return "__{}__".format(text)
|
||||||
|
|
||||||
|
|
||||||
def escape(text, *, mass_mentions=False, formatting=False):
|
def escape(text: str, *, mass_mentions: bool=False,
|
||||||
|
formatting: bool=False) -> str:
|
||||||
|
"""Get text with all mass mentions or markdown escaped.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text : str
|
||||||
|
The text to be escaped.
|
||||||
|
mass_mentions : `bool`, optional
|
||||||
|
Set to :code:`True` to escape mass mentions in the text.
|
||||||
|
formatting : `bool`, optional
|
||||||
|
Set to :code:`True` to escpae any markdown formatting in the text.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The escaped text.
|
||||||
|
|
||||||
|
"""
|
||||||
if mass_mentions:
|
if mass_mentions:
|
||||||
text = text.replace("@everyone", "@\u200beveryone")
|
text = text.replace("@everyone", "@\u200beveryone")
|
||||||
text = text.replace("@here", "@\u200bhere")
|
text = text.replace("@here", "@\u200bhere")
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import List
|
from datetime import timedelta
|
||||||
|
from typing import List, Iterable, Union
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -9,6 +10,32 @@ from redbot.core.bot import Red
|
|||||||
|
|
||||||
async def mass_purge(messages: List[discord.Message],
|
async def mass_purge(messages: List[discord.Message],
|
||||||
channel: discord.TextChannel):
|
channel: discord.TextChannel):
|
||||||
|
"""Bulk delete messages from a channel.
|
||||||
|
|
||||||
|
If more than 100 messages are supplied, the bot will delete 100 messages at
|
||||||
|
a time, sleeping between each action.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
|
Messages must not be older than 14 days, and the bot must not be a user
|
||||||
|
account.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
messages : `list` of `discord.Message`
|
||||||
|
The messages to bulk delete.
|
||||||
|
channel : discord.TextChannel
|
||||||
|
The channel to delete messages from.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
discord.Forbidden
|
||||||
|
You do not have proper permissions to delete the messages or you’re not
|
||||||
|
using a bot account.
|
||||||
|
discord.HTTPException
|
||||||
|
Deleting the messages failed.
|
||||||
|
|
||||||
|
"""
|
||||||
while messages:
|
while messages:
|
||||||
if len(messages) > 1:
|
if len(messages) > 1:
|
||||||
await channel.delete_messages(messages[:100])
|
await channel.delete_messages(messages[:100])
|
||||||
@ -19,7 +46,17 @@ async def mass_purge(messages: List[discord.Message],
|
|||||||
await asyncio.sleep(1.5)
|
await asyncio.sleep(1.5)
|
||||||
|
|
||||||
|
|
||||||
async def slow_deletion(messages: List[discord.Message]):
|
async def slow_deletion(messages: Iterable[discord.Message]):
|
||||||
|
"""Delete a list of messages one at a time.
|
||||||
|
|
||||||
|
Any exceptions raised when trying to delete the message will be silenced.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
messages : `iterable` of `discord.Message`
|
||||||
|
The messages to delete.
|
||||||
|
|
||||||
|
"""
|
||||||
for message in messages:
|
for message in messages:
|
||||||
try:
|
try:
|
||||||
await message.delete()
|
await message.delete()
|
||||||
@ -28,23 +65,62 @@ async def slow_deletion(messages: List[discord.Message]):
|
|||||||
|
|
||||||
|
|
||||||
def get_audit_reason(author: discord.Member, reason: str = None):
|
def get_audit_reason(author: discord.Member, reason: str = None):
|
||||||
"""Helper function to construct a reason to be provided
|
"""Construct a reason to appear in the audit log.
|
||||||
as the reason to appear in the audit log."""
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
author : discord.Member
|
||||||
|
The author behind the audit log action.
|
||||||
|
reason : str
|
||||||
|
The reason behidn the audit log action.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The formatted audit log reason.
|
||||||
|
|
||||||
|
"""
|
||||||
return \
|
return \
|
||||||
"Action requested by {} (ID {}). Reason: {}".format(author, author.id, reason) if reason else \
|
"Action requested by {} (ID {}). Reason: {}".format(author, author.id, reason) if reason else \
|
||||||
"Action requested by {} (ID {}).".format(author, author.id)
|
"Action requested by {} (ID {}).".format(author, author.id)
|
||||||
|
|
||||||
|
|
||||||
async def is_allowed_by_hierarchy(
|
async def is_allowed_by_hierarchy(bot: Red,
|
||||||
bot: Red, settings: Config, server: discord.Guild,
|
settings: Config,
|
||||||
mod: discord.Member, user: discord.Member):
|
guild: discord.Guild,
|
||||||
if not await settings.guild(server).respect_hierarchy():
|
mod: discord.Member,
|
||||||
|
user: discord.Member):
|
||||||
|
if not await settings.guild(guild).respect_hierarchy():
|
||||||
return True
|
return True
|
||||||
is_special = mod == server.owner or await bot.is_owner(mod)
|
is_special = mod == guild.owner or await bot.is_owner(mod)
|
||||||
return mod.top_role.position > user.top_role.position or is_special
|
return mod.top_role.position > user.top_role.position or is_special
|
||||||
|
|
||||||
|
|
||||||
async def is_mod_or_superior(bot: Red, obj: discord.Message or discord.Member or discord.Role):
|
async def is_mod_or_superior(
|
||||||
|
bot: Red, obj: Union[discord.Message, discord.Member, discord.Role]):
|
||||||
|
"""Check if an object has mod or superior permissions.
|
||||||
|
|
||||||
|
If a message is passed, its author's permissions are checked. If a role is
|
||||||
|
passed, it simply checks if it is one of either the admin or mod roles.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
bot : redbot.core.bot.Red
|
||||||
|
The bot object.
|
||||||
|
obj : `discord.Message` or `discord.Member` or `discord.Role`
|
||||||
|
The object to check permissions for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
:code:`True` if the object has mod permissions.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
TypeError
|
||||||
|
If the wrong type of ``obj`` was passed.
|
||||||
|
|
||||||
|
"""
|
||||||
user = None
|
user = None
|
||||||
if isinstance(obj, discord.Message):
|
if isinstance(obj, discord.Message):
|
||||||
user = obj.author
|
user = obj.author
|
||||||
@ -76,7 +152,20 @@ async def is_mod_or_superior(bot: Red, obj: discord.Message or discord.Member or
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def strfdelta(delta):
|
def strfdelta(delta: timedelta):
|
||||||
|
"""Format a timedelta object to a message with time units.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
delta : datetime.timedelta
|
||||||
|
The duration to parse.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
A message representing the timedelta with units.
|
||||||
|
|
||||||
|
"""
|
||||||
s = []
|
s = []
|
||||||
if delta.days:
|
if delta.days:
|
||||||
ds = '%i day' % delta.days
|
ds = '%i day' % delta.days
|
||||||
@ -97,7 +186,31 @@ def strfdelta(delta):
|
|||||||
return ' '.join(s)
|
return ' '.join(s)
|
||||||
|
|
||||||
|
|
||||||
async def is_admin_or_superior(bot: Red, obj: discord.Message or discord.Role or discord.Member):
|
async def is_admin_or_superior(
|
||||||
|
bot: Red, obj: Union[discord.Message, discord.Member, discord.Role]):
|
||||||
|
"""Same as `is_mod_or_superior` except for admin permissions.
|
||||||
|
|
||||||
|
If a message is passed, its author's permissions are checked. If a role is
|
||||||
|
passed, it simply checks if it is the admin role.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
bot : redbot.core.bot.Red
|
||||||
|
The bot object.
|
||||||
|
obj : `discord.Message` or `discord.Member` or `discord.Role`
|
||||||
|
The object to check permissions for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
:code:`True` if the object has admin permissions.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
TypeError
|
||||||
|
If the wrong type of ``obj`` was passed.
|
||||||
|
|
||||||
|
"""
|
||||||
user = None
|
user = None
|
||||||
if isinstance(obj, discord.Message):
|
if isinstance(obj, discord.Message):
|
||||||
user = obj.author
|
user = obj.author
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user