Add support for voice channel parties #33

Closed
wasi-master wants to merge 277 commits from master into 2.0
45 changed files with 7313 additions and 145 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@ docs/crowdin.py
*.jpg
*.flac
*.mo
dist
build

View File

@ -1,17 +1,19 @@
discord.py
==========
Enhanced-dpy (custom discord.py)
=================================
.. image:: https://discord.com/api/guilds/336642139381301249/embed.png
:target: https://discord.gg/r3sSKJJ
:alt: Discord server invite
.. image:: https://img.shields.io/pypi/v/discord.py.svg
:target: https://pypi.python.org/pypi/discord.py
:alt: PyPI version info
.. image:: https://img.shields.io/pypi/pyversions/discord.py.svg
:target: https://pypi.python.org/pypi/discord.py
:alt: PyPI supported Python versions
A modern, easy to use, feature-rich, and async ready API wrapper for Discord written in Python.
Credits to the `original lib by Rapptz <https://github.com/Rapptz/discord.py>`_
**WARNING: This is not the official discord.py library! As of 8/27/2021 Danny (Rapptz) has stopped development due to breaking changes. You are still able to read the official library at https://github.com/Rapptz/discord.py!**
Custom Features
----------------
**Moved to:** `Custom Features <https://enhanced-dpy.readthedocs.io/en/latest/custom_features.html>`_
Key Features
-------------
@ -31,28 +33,17 @@ To install the library without full voice support, you can just run the followin
.. code:: sh
# Linux/macOS
python3 -m pip install -U discord.py
python3 -m pip install -U enhanced-dpy
# Windows
py -3 -m pip install -U discord.py
Otherwise to get voice support you should run the following command:
.. code:: sh
# Linux/macOS
python3 -m pip install -U "discord.py[voice]"
# Windows
py -3 -m pip install -U discord.py[voice]
py -3 -m pip install -U enhanced-dpy
To install the development version, do the following:
.. code:: sh
$ git clone https://github.com/Rapptz/discord.py
$ cd discord.py
$ git clone https://github.com/iDevision/enhanced-discord.py
$ cd enhanced-discord.py
$ python3 -m pip install -U .[voice]
@ -109,6 +100,6 @@ You can find more examples in the examples directory.
Links
------
- `Documentation <https://discordpy.readthedocs.io/en/latest/index.html>`_
- `Official Discord Server <https://discord.gg/r3sSKJJ>`_
- `Documentation <https://enhanced-dpy.readthedocs.io/en/latest/index.html>`_
- `Official Discord Server <https://discord.gg/wZSH7pz>`_
- `Discord API <https://discord.gg/discord-api>`_

View File

@ -13,7 +13,7 @@ __title__ = 'discord'
__author__ = 'Rapptz'
__license__ = 'MIT'
__copyright__ = 'Copyright 2015-present Rapptz'
__version__ = '2.0.0a'
__version__ = '1.7.3.7.post1'
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
@ -57,8 +57,8 @@ from .team import *
from .sticker import *
from .interactions import *
VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
VersionInfo = namedtuple('VersionInfo', 'major minor micro enhanced releaselevel serial')
version_info = VersionInfo(major=2, minor=0, micro=0, releaselevel='alpha', serial=0)
version_info = VersionInfo(major=1, minor=7, micro=3, enhanced=7, releaselevel='final', serial=0)
logging.getLogger(__name__).addHandler(logging.NullHandler())

View File

@ -176,6 +176,7 @@ class GuildChannel(Protocol):
- :class:`~discord.TextChannel`
- :class:`~discord.VoiceChannel`
- :class:`~discord.CategoryChannel`
- :class:`~discord.StageChannel`
This ABC must also implement :class:`~discord.abc.Snowflake`.
@ -199,6 +200,9 @@ class GuildChannel(Protocol):
def __str__(self):
return self.name
def __int__(self):
return self.id
@property
def _sorting_bucket(self):
raise NotImplementedError
@ -734,10 +738,10 @@ class GuildChannel(Protocol):
Whether to move the channel to the end of the
channel list (or category if given).
This is mutually exclusive with ``beginning``, ``before``, and ``after``.
before: :class:`abc.Snowflake`
before: :class:`~discord.abc.Snowflake`
The channel that should be before our current channel.
This is mutually exclusive with ``beginning``, ``end``, and ``after``.
after: :class:`abc.Snowflake`
after: :class:`~discord.abc.Snowflake`
The channel that should be after our current channel.
This is mutually exclusive with ``beginning``, ``end``, and ``before``.
offset: :class:`int`
@ -747,7 +751,7 @@ class GuildChannel(Protocol):
while a negative number moves it above. Note that this
number is relative and computed after the ``beginning``,
``end``, ``before``, and ``after`` parameters.
category: Optional[:class:`abc.Snowflake`]
category: Optional[:class:`~discord.abc.Snowflake`]
The category to move this channel under.
If ``None`` is given then it moves it out of the category.
This parameter is ignored if moving a category channel.
@ -974,6 +978,12 @@ class Messageable(Protocol):
are used instead.
.. versionadded:: 1.4
message_reference: :class:`~discord.MessageReference`
A reference to the :class:`~discord.Message` to which you are replying, i.e. as created using
:meth:`~discord.MessageReference.from_message`. You can control whether this mentions the author
of the referenced Message using :attr:`~discord.AllowedMentions.replied_user`.
.. versionadded:: 1.5.1.5
reference: Union[:class:`~discord.Message`, :class:`~discord.MessageReference`]
A reference to the :class:`~discord.Message` to which you are replying, this can be created using

View File

@ -62,30 +62,32 @@ class AppInfo:
A list of RPC origin URLs, if RPC is enabled.
summary: :class:`str`
If this application is a game sold on Discord,
this field will be the summary field for the store page of its primary SKU
this field will be the summary field for the store page of its primary SKU.
.. versionadded:: 1.3
verify_key: :class:`str`
The base64 encoded key for the GameSDK's GetTicket
The hex encoded key for verification in interactions and the
GameSDK's `GetTicket <https://discord.com/developers/docs/game-sdk/applications#getticket>`_.
.. versionadded:: 1.3
guild_id: Optional[:class:`int`]
If this application is a game sold on Discord,
this field will be the guild to which it has been linked
this field will be the guild to which it has been linked to.
.. versionadded:: 1.3
primary_sku_id: Optional[:class:`int`]
If this application is a game sold on Discord,
this field will be the id of the "Game SKU" that is created, if exists
this field will be the id of the "Game SKU" that is created,
if it exists.
.. versionadded:: 1.3
slug: Optional[:class:`str`]
If this application is a game sold on Discord,
this field will be the URL slug that links to the store page
this field will be the URL slug that links to the store page.
.. versionadded:: 1.3

View File

@ -27,7 +27,7 @@ import asyncio
import discord.abc
from .permissions import Permissions
from .enums import ChannelType, try_enum, VoiceRegion
from .enums import ChannelType, try_enum, VoiceRegion, PartyType
from .mixins import Hashable
from . import utils
from .asset import Asset
@ -141,6 +141,13 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
def _sorting_bucket(self):
return ChannelType.text.value
@property
def can_send(self):
""":class:`bool`: Checks if the bot can send messages
.. versionadded:: 1.5.0.2"""
return self.permissions_for(self.guild.me).send_messages
@utils.copy_doc(discord.abc.GuildChannel.permissions_for)
def permissions_for(self, member):
base = super().permissions_for(member)
@ -727,6 +734,37 @@ class VoiceChannel(VocalGuildChannel):
await self._edit(options, reason=reason)
async def create_party(self, application_id: PartyType, max_age: int = 86400 , max_uses: int = 0):
"""|coro|
Creates a party in this voice channel.
.. versionadded:: 1.7.3.8
Parameters
----------
application_id : PartyType
The id of the application the party belongs to. currently any of
``PartyType.youtube``, ``PartyType.poker``, ``PartyType.betrayal``, ``PartyType.fishing``, ``PartyType.chess``.
max_age : int, optional
Duration in seconds after which the invite expires, by default 1 day.
max_uses : int, optional
maximum number of times this invite can be used, by default Unlimited.
Raises
-------
Forbidden
You do not have permissions to create a party.
HTTPException
Party creation failed.
Returns
--------
:class:`Party`
The created party.
"""
return Party(await self._state.http.create_party(self.id, application_id.value, max_age=max_age, max_uses=max_uses))
class StageChannel(VocalGuildChannel):
"""Represents a Discord guild stage channel.
@ -1255,6 +1293,7 @@ class DMChannel(discord.abc.Messageable, Hashable):
"""
base = Permissions.text()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
return base
@ -1428,6 +1467,7 @@ class GroupChannel(discord.abc.Messageable, Hashable):
"""
base = Permissions.text()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
base.mention_everyone = True
@ -1452,6 +1492,29 @@ class GroupChannel(discord.abc.Messageable, Hashable):
await self._state.http.leave_group(self.id)
class Party:
"""Represents a party in a voice channel."""
__slots__ = ('code', 'uses', 'max_uses', 'max_age', 'temporary', 'created_at')
def __init__(self, data):
self.code = data['code']
self.uses = data['uses']
self.max_uses = data['max_uses']
self.max_age = data['max_age']
self.temporary = data['temporary']
self.created_at = utils.parse_time(data.get('created_at'))
# TODO: add more fields here Such as guild, raw data: https://mystb.in/AdvertisersExperiencesMothers.json
def __repr__(self):
return f'<Party code={self.code}>'
def __str__(self):
return f'https://discord.gg/{self.code}'
def __eq__(self, other):
return isinstance(other, Party) and self.code == other.code
def _channel_factory(channel_type):
value = try_enum(ChannelType, channel_type)
if value is ChannelType.text:

View File

@ -24,33 +24,35 @@ DEALINGS IN THE SOFTWARE.
import asyncio
import logging
import os
import re
import signal
import sys
import traceback
import aiohttp
from .user import User
from .invite import Invite
from .template import Template
from .widget import Widget
from .guild import Guild
from .channel import _channel_factory
from .enums import ChannelType
from .mentions import AllowedMentions
from .errors import *
from .enums import Status, VoiceRegion
from .gateway import *
from .activity import BaseActivity, create_activity
from .voice_client import VoiceClient
from .http import HTTPClient
from .state import ConnectionState
from . import utils
from .object import Object
from .backoff import ExponentialBackoff
from .webhook import Webhook
from .iterators import GuildIterator
from .activity import BaseActivity, create_activity
from .appinfo import AppInfo
from .backoff import ExponentialBackoff
from .channel import _channel_factory
from .colour import Color, Colour
from .enums import ChannelType, Status, VoiceRegion
from .errors import *
from .gateway import *
from .guild import Guild
from .http import HTTPClient
from .invite import Invite
from .iterators import GuildIterator
from .mentions import AllowedMentions
from .object import Object
from .state import ConnectionState
from .template import Template
from .user import User
from .voice_client import VoiceClient
from .webhook import Webhook
from .widget import Widget
__all__ = (
'Client',
@ -167,7 +169,7 @@ class Client:
If this is set to ``False`` then the following features will be disabled:
- No user related updates (:func:`on_user_update` will not dispatch)
- No user related updates (:func:`on_user_update` will not dispatch)EmptyEmbed
- All member related events will be disabled.
- :func:`on_member_update`
- :func:`on_member_join`
@ -197,6 +199,12 @@ class Client:
sync your system clock to Google's NTP server.
.. versionadded:: 1.3
embed_color: Union[:class:`.Colour`, :class:`int`]
The default embed color you want to use when initialising a :class:`.Embed`. This will
remove the need to set the color per embed, but can still be overridden by setting a
color while creating an instance of an embed.
.. versionadded:: 1.5.0.1
Attributes
-----------
@ -211,6 +219,19 @@ class Client:
self._listeners = {}
self.shard_id = options.get('shard_id')
self.shard_count = options.get('shard_count')
colour = options.get('embed_color', Color.default())
if isinstance(colour, (Color, Colour)):
os.environ['DEFAULT_EMBED_COLOR'] = str(hex(colour))
else:
try:
HEX = re.compile(r'^(#)[A-Fa-f0-9]{6}$')
col = Color(colour)
if HEX.match(str(col)):
os.environ['DEFAULT_EMBED_COLOR'] = str(hex(col))
else:
raise TypeError('The hex value passed could not be converted to a color')
except:
raise TypeError('embed_color must be an instance of discord.Colour or a valid 0x****** hex value.')
connector = options.pop('connector', None)
proxy = options.pop('proxy', None)
@ -239,6 +260,65 @@ class Client:
# internals
def get_message(self, id):
"""Get a message from cache if the message is still in cache.
.. versionadded:: 1.6.0.7
Parameters
-----------
id: :class:`int`
The message ID to look for.
Returns
--------
Optional[:class:`.Message`]
The message asked for."""
return utils.get(self.cached_messages, id=id)
@property
def embed_color(self):
"""Optional[:class:`.Colour`]: The default embed colour that is
being used for all embed if no colour is passed."""
col = os.getenv("DEFAULT_EMBED_COLOR")
if not col:
return None
return Colour(int(col, 16))
def set_embed_color(self, color):
"""Set a new default embed color.
This will raise a TypeError if an improper format was passed.
.. versionadded:: 1.5.0.1
Parameters
-----------
color: Union[:class:`.Colour`, :class:`int`]
The new color you want to set as default embed color.
Pass either an instance of discord.Color or a valid
0x****** HEX value.
Returns
--------
:class:`.Colour`
The new color that has been set as default embed color.
"""
if isinstance(color, (Color, Colour)):
os.environ['DEFAULT_EMBED_COLOR'] = str(hex(color))
return color
else:
try:
HEX = re.compile(r'^(#)[A-Fa-f0-9]{6}$')
col = Color(color)
if HEX.match(str(col)):
os.environ['DEFAULT_EMBED_COLOR'] = str(hex(col))
return col
else:
raise TypeError('The hex value passed could not be converted to a color')
except:
raise TypeError('embed_color must be an instance of discord.Colour or a valid 0x****** hex value.')
def _get_websocket(self, guild_id=None, *, shard_id=None):
return self.ws
@ -690,7 +770,7 @@ class Client:
@property
def intents(self):
""":class:`Intents`: The intents configured for this connection.
""":class:`~discord.Intents`: The intents configured for this connection.
.. versionadded:: 1.5
"""
@ -1278,6 +1358,42 @@ class Client:
data['rpc_origins'] = None
return AppInfo(self._connection, data)
async def try_user(self, user_id):
"""|coro|
Retrieves a :class:`~discord.User` based on their ID. This can only
be used by bot accounts.
.. versionadded:: 1.5.0.1
.. note::
This will first attempt to get the user from the cache.
If that fails, it will make an API call.
For general usage, consider :meth:`get_user` instead.
Parameters
-----------
user_id: :class:`int`
The user's ID to fetch from.
Raises
-------
:exc:`.NotFound`
A user with this ID does not exist.
:exc:`.HTTPException`
Fetching the user failed.
Returns
--------
:class:`~discord.User`
The user you requested.
"""
user = self.get_user(user_id)
if user is None:
user = await self.fetch_user(user_id)
return user
async def fetch_user(self, user_id):
"""|coro|
@ -1288,7 +1404,7 @@ class Client:
.. note::
This method is an API call. For general usage, consider :meth:`get_user` instead.
This method is an API call. If you have :attr:`Intents.members` and member cache enabled, consider :meth:`get_user` instead.
Parameters
-----------

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
import datetime
import os
from typing import Any, Dict, Final, List, Protocol, TYPE_CHECKING, Type, TypeVar, Union
from . import utils
@ -186,7 +187,13 @@ class Embed:
timestamp: datetime.datetime = None,
):
self.colour = colour if colour is not EmptyEmbed else color
if colour is EmptyEmbed and color is EmptyEmbed:
colour = os.getenv("DEFAULT_EMBED_COLOR", default=EmptyEmbed)
if isinstance(colour, str):
colour = int(colour, 16)
else:
colour = colour if colour is not EmptyEmbed else color
self.colour = colour
self.title = title
self.type = type
self.url = url

View File

@ -114,6 +114,9 @@ class Emoji(_EmojiTag):
return '<a:{0.name}:{0.id}>'.format(self)
return "<:{0.name}:{0.id}>".format(self)
def __int__(self):
return self.id
def __repr__(self):
return '<Emoji id={0.id} name={0.name!r} animated={0.animated} managed={0.managed}>'.format(self)

View File

@ -30,6 +30,7 @@ __all__ = (
'Enum',
'ChannelType',
'MessageType',
'PartyType',
'VoiceRegion',
'SpeakingState',
'VerificationLevel',
@ -179,6 +180,13 @@ class MessageType(Enum):
guild_discovery_grace_period_initial_warning = 16
guild_discovery_grace_period_final_warning = 17
class PartyType(Enum):
youtube = 755600276941176913
poker = 755827207812677713
betrayal = 773336526917861400
fishing = 814288819477020702
chess = 832012774040141894
class VoiceRegion(Enum):
us_west = 'us-west'
us_east = 'us-east'
@ -368,6 +376,8 @@ class AuditLogAction(Enum):
return 'webhook'
elif v < 70:
return 'emoji'
elif v == 73:
return 'channel'
elif v < 80:
return 'message'
elif v < 90:

View File

@ -24,20 +24,21 @@ DEALINGS IN THE SOFTWARE.
import asyncio
import collections
import inspect
import importlib.util
import inspect
import itertools
import sys
import traceback
import types
import discord
from .core import GroupMixin
from .view import StringView
from .context import Context
from . import errors
from .help import HelpCommand, DefaultHelpCommand
from .cog import Cog
from .context import Context
from .core import GroupMixin
from .help import DefaultHelpCommand, HelpCommand
from .view import StringView
__all__ = (
'when_mentioned',
@ -99,9 +100,10 @@ class _DefaultRepr:
_default = _DefaultRepr()
class BotBase(GroupMixin):
def __init__(self, command_prefix, help_command=_default, description=None, **options):
def __init__(self, command_prefix, case_insensitive_prefix=False, help_command=_default, description=None, **options):
super().__init__(**options)
self.command_prefix = command_prefix
self.case_insensitive_prefix = case_insensitive_prefix
self.extra_events = {}
self.__cogs = {}
self.__extensions = {}
@ -131,6 +133,29 @@ class BotBase(GroupMixin):
else:
self.help_command = help_command
@property
def owner(self):
""":class:`discord.User`: The owner, retrieved from owner_id. In case of improper caching, this can return None
.. versionadded:: 1.5.0.1"""
if not self.owner_id or self.owner_ids:
raise AttributeError('No owner_id specified or you used owner_ids. If you used owner_ids, please refer to `Bot.owners`')
return self.get_user(self.owner_id)
@property
def owners(self):
"""List[:class:`discord.User`]: The owners, retrieved from owner_ids. In case of improper caching, this list may not contain all owners.
.. versionadded:: 1.5.0.1"""
if not self.owner_ids or self.owner_id:
raise TypeError('No owner_ids specified or you used owner_id. If you used owner_id, please refer to `Bot.owner`')
owners = []
for user in self.owner_ids:
owner = self.get_user(user)
if owner:
owners.append(owner)
return owners
# internal helpers
def dispatch(self, event_name, *args, **kwargs):
@ -511,6 +536,9 @@ class BotBase(GroupMixin):
cog = cog._inject(self)
self.__cogs[cog.__cog_name__] = cog
if cog.aliases:
for alias in cog.aliases:
self.__cogs[alias] = cog
def get_cog(self, name):
"""Gets the cog instance requested.
@ -549,6 +577,10 @@ class BotBase(GroupMixin):
if cog is None:
return
if cog.aliases:
for alias in cog.aliases:
self.__cogs.pop(alias)
help_command = self._help_command
if help_command and help_command.cog is cog:
help_command.cog = None
@ -850,6 +882,16 @@ class BotBase(GroupMixin):
if not ret:
raise ValueError("Iterable command_prefix must contain at least one prefix")
# if self.case_insensitive_prefix:
# if isinstance(ret, list):
# temp = []
# for pre in ret:
# if pre in (self.user.mention + ' ', '<@!%s> ' % self.user.id):
# continue
# temp += list(map(''.join, itertools.product(*((c.upper(), c.lower()) for c in pre))))
# ret = temp
# else:
# ret = list(map(''.join, itertools.product(*((c.upper(), c.lower()) for c in ret))))
return ret
async def get_context(self, message, *, cls=Context):
@ -1024,6 +1066,10 @@ class Bot(BotBase, discord.Client):
matches messages starting with ``!?``. This is especially important
when passing an empty string, it should always be last as no prefix
after it will be matched.
case_insensitive_prefix: :class:`bool`
Wheter the provided command_prefix should be case insensitive or not
.. versionadded:: 1.6.0.7
case_insensitive: :class:`bool`
Whether the commands should be case insensitive. Defaults to ``False``. This
attribute does not carry over to groups. You must set it to every group if

View File

@ -89,6 +89,11 @@ class CogMeta(type):
@commands.command(hidden=False)
async def bar(self, ctx):
pass # hidden -> False
aliases: :class:`list`
A list of aliases for the cog name.
.. versionadded:: 1.6.0.7
"""
def __new__(cls, *args, **kwargs):
@ -96,6 +101,11 @@ class CogMeta(type):
attrs['__cog_name__'] = kwargs.pop('name', name)
attrs['__cog_settings__'] = kwargs.pop('command_attrs', {})
aliases = kwargs.pop('aliases', [])
if not isinstance(aliases, list):
raise TypeError("Cog aliases must be a list, not a {0}".format(type(aliases)))
attrs['aliases'] = aliases
description = kwargs.pop('description', None)
if description is None:
description = inspect.cleandoc(attrs.get('__doc__', ''))

View File

@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
import re
import discord.abc
import discord.utils
@ -95,6 +96,15 @@ class Context(discord.abc.Messageable):
self.command_failed = attrs.pop('command_failed', False)
self._state = self.message._state
@property
def clean_prefix(self):
""":class:`str`: The cleaned up invoke prefix. i.e. mentions are ``@name`` instead of ``<@id>``.
.. versionadded:: 1.5.1.4"""
user = self.guild.me if self.guild else self.bot.user
pattern = re.compile(r"<@!?%s>" % user.id)
return pattern.sub("@%s" % user.display_name.replace('\\', r'\\'), self.prefix)
async def invoke(self, command, /, *args, **kwargs):
r"""|coro|

View File

@ -157,7 +157,7 @@ class MemberConverter(IDConverter[discord.Member]):
# If we're being rate limited on the WS, then fall back to using the HTTP API
# So we don't have to wait ~60 seconds for the query to finish
try:
member = await guild.fetch_member(user_id)
member = await guild.try_member(user_id)
except discord.HTTPException:
return None
@ -233,7 +233,7 @@ class UserConverter(IDConverter[discord.User]):
result = ctx.bot.get_user(user_id) or _utils_get(ctx.message.mentions, id=user_id)
if result is None:
try:
result = await ctx.bot.fetch_user(user_id)
result = await ctx.bot.try_user(user_id)
except discord.HTTPException:
raise UserNotFound(argument) from None
@ -617,8 +617,8 @@ ColorConverter = ColourConverter
class RoleConverter(IDConverter[discord.Role]):
"""Converts to a :class:`~discord.Role`.
All lookups are via the local guild. If in a DM context, then the lookup
is done by the global cache.
All lookups are via the local guild. If in a DM context, the converter raises
:exc:`.NoPrivateMessage` exception.
The lookup strategy is as follows (in order):

View File

@ -276,7 +276,7 @@ class HelpCommand:
If ``None``, only calls :attr:`.Commands.checks` in a guild setting.
If ``False``, never calls :attr:`.Commands.checks`. Defaults to ``True``.
..versionchanged:: 1.7
.. versionchanged:: 1.7
command_attrs: :class:`dict`
A dictionary of options to pass in for the construction of the help command.
This allows you to change the command behaviour without actually changing

File diff suppressed because it is too large Load Diff

View File

@ -111,13 +111,13 @@ class Loop:
raise
await asyncio.sleep(backoff.delay())
else:
await sleep_until(self._next_iteration)
if self._stop_next_iteration:
return
self._current_loop += 1
if self._current_loop == self.count:
break
await sleep_until(self._next_iteration)
except asyncio.CancelledError:
self._is_being_cancelled = True
raise

View File

@ -422,6 +422,22 @@ class Intents(BaseFlags):
raise TypeError(f'{key!r} is not a valid flag name.')
setattr(self, key, value)
@classmethod
def from_list(cls, intents_list):
"""A factory method that creates a :class:`Intents` with everything enabled
that has been passed in the list.
.. versionadded:: 1.5.0.1"""
for item in intents_list:
if item not in cls.VALID_FLAGS.keys():
intents_list.remove(item)
self = cls.none()
for item in intents_list:
setattr(self, item, True)
return self
@classmethod
def all(cls: Type[Intents]) -> Intents:
"""A factory method that creates a :class:`Intents` with everything enabled."""
@ -593,6 +609,7 @@ class Intents(BaseFlags):
@flag_value
def presences(self):
""":class:`bool`: Whether guild presence related events are enabled.
This corresponds to the following events:

View File

@ -26,23 +26,23 @@ import copy
from collections import namedtuple
from . import utils
from .role import Role
from .member import Member, VoiceState
from .emoji import Emoji
from .errors import InvalidData
from .permissions import PermissionOverwrite
from .colour import Colour
from .errors import InvalidArgument, ClientException
from .channel import *
from .enums import VoiceRegion, ChannelType, try_enum, VerificationLevel, ContentFilter, NotificationLevel
from .mixins import Hashable
from .user import User
from .invite import Invite
from .iterators import AuditLogIterator, MemberIterator
from .widget import Widget
from .asset import Asset
from .channel import *
from .colour import Colour
from .emoji import Emoji
from .enums import (ChannelType, ContentFilter, NotificationLevel,
VerificationLevel, VoiceRegion, try_enum)
from .errors import ClientException, InvalidArgument, InvalidData
from .flags import SystemChannelFlags
from .integrations import Integration
from .invite import Invite
from .iterators import AuditLogIterator, MemberIterator
from .member import Member, VoiceState
from .mixins import Hashable
from .permissions import PermissionOverwrite
from .role import Role
from .user import User
from .widget import Widget
__all__ = (
'Guild',
@ -96,7 +96,7 @@ class Guild(Hashable):
The guild owner's ID. Use :attr:`Guild.owner` instead.
unavailable: :class:`bool`
Indicates if the guild is unavailable. If this is ``True`` then the
reliability of other attributes outside of :meth:`Guild.id` is slim and they might
reliability of other attributes outside of :attr:`Guild.id` is slim and they might
all be ``None``. It is best to not do anything with the guild if it is unavailable.
Check the :func:`on_guild_unavailable` and :func:`on_guild_available` events.
@ -208,6 +208,9 @@ class Guild(Hashable):
def __str__(self):
return self.name or ''
def __int__(self):
return self.id
def __repr__(self):
attrs = (
('id', self.id),
@ -531,6 +534,20 @@ class Guild(Hashable):
"""List[:class:`Member`]: A list of members that belong to this guild."""
return list(self._members.values())
@property
def bots(self):
"""List[:class:`Member`]: A list of bots that belong to this guild.
.. versionadded:: 1.5.0.1"""
return list(m for m in self._members.values() if m.bot)
@property
def humans(self):
"""List[:class:`Member`]: A list of humans that belong to this guild.
.. versionadded:: 1.5.0.1"""
return list(m for m in self._members.values() if not m.bot)
def get_member(self, user_id):
"""Returns a member with the given ID.
@ -611,8 +628,8 @@ class Guild(Hashable):
@property
def icon_url(self):
""":class:`Asset`: Returns the guild's icon asset."""
return self.icon_url_as()
""":class:`str`: Returns the guild's direct icon url."""
return str(self.icon_url_as(static_format="png"))
def is_icon_animated(self):
""":class:`bool`: Returns True if the guild has an animated icon."""
@ -1112,8 +1129,8 @@ class Guild(Hashable):
The new description of the guild. This is only available to guilds that
contain ``PUBLIC`` in :attr:`Guild.features`.
icon: :class:`bytes`
A :term:`py:bytes-like object` representing the icon. Only PNG/JPEG supported
and GIF This is only available to guilds that contain ``ANIMATED_ICON`` in :attr:`Guild.features`.
A :term:`py:bytes-like object` representing the icon. Only PNG/JPEG is supported.
GIF is only available to guilds that contain ``ANIMATED_ICON`` in :attr:`Guild.features`.
Could be ``None`` to denote removal of the icon.
banner: :class:`bytes`
A :term:`py:bytes-like object` representing the banner.
@ -1313,7 +1330,7 @@ class Guild(Hashable):
def convert(d):
factory, ch_type = _channel_factory(d['type'])
if factory is None:
raise InvalidData('Unknown channel type {type} for channel ID {id}.'.format_map(data))
raise InvalidData('Unknown channel type {type} for channel ID {id}.'.format_map(d))
channel = factory(guild=self, state=self._state, data=d)
return channel
@ -1373,6 +1390,42 @@ class Guild(Hashable):
return MemberIterator(self, limit=limit, after=after)
async def try_member(self, member_id):
"""|coro|
Retreives a :class:`Member` from a guild ID, and a member ID.
.. versionadded:: 1.5.0.2
.. note::
This will first attempt to get the member from the cache.
If that fails, it will make an API call.
This method is an API call. For general usage, consider :meth:`get_member` instead.
Parameters
-----------
member_id: :class:`int`
The member's ID to fetch from.
Raises
-------
Forbidden
You do not have access to the guild.
HTTPException
Fetching the member failed.
Returns
--------
:class:`Member`
The member from the member ID.
"""
member = self.get_member(member_id)
if member is None:
member = await self.fetch_member(member_id)
return member
async def fetch_member(self, member_id):
"""|coro|
@ -1380,7 +1433,7 @@ class Guild(Hashable):
.. note::
This method is an API call. For general usage, consider :meth:`get_member` instead.
This method is an API call. If you have :attr:`Intents.members` and member cache enabled, consider :meth:`get_member` instead.
Parameters
-----------
@ -2207,7 +2260,8 @@ class Guild(Hashable):
if not self._state._intents.members:
raise ClientException('Intents.members must be enabled to use this.')
return await self._state.chunk_guild(self, cache=cache)
if not self._state.is_guild_evicted(self):
return await self._state.chunk_guild(self, cache=cache)
async def query_members(self, query=None, *, limit=5, user_ids=None, presences=False, cache=True):
"""|coro|

View File

@ -701,6 +701,17 @@ class HTTPClient:
def delete_channel(self, channel_id, *, reason=None):
return self.request(Route('DELETE', '/channels/{channel_id}', channel_id=channel_id), reason=reason)
def create_party(self, channel_id, application_id, max_age: int, max_uses: int):
payload = {
'max_age': max_age,
'max_uses': max_uses,
'target_application_id': application_id,
'target_type': 2,
'temporary': False,
'validate': None
}
return self.request(Route("POST", "/channels/{channel_id}/invites", channel_id=channel_id), json=payload)
# Webhook management
def create_webhook(self, channel_id, *, name, avatar=None, reason=None):

View File

@ -192,6 +192,13 @@ class Member(discord.abc.Messageable, _BaseUser):
If the member left and rejoined the guild, this will be the latest date. In certain cases, this can be ``None``.
activities: Tuple[Union[:class:`BaseActivity`, :class:`Spotify`]]
The activities that the user is currently doing.
.. note::
Due to a Discord API limitation, a user's Spotify activity may not appear
if they are listening to a song with a title longer
than 128 characters. See :issue:`1738` for more information.
guild: :class:`Guild`
The guild that the member belongs to.
nick: Optional[:class:`str`]
@ -225,6 +232,9 @@ class Member(discord.abc.Messageable, _BaseUser):
def __str__(self):
return str(self._user)
def __int__(self):
return self.id
def __repr__(self):
return f'<Member id={self._user.id} name={self._user.name!r} discriminator={self._user.discriminator!r}' \
f' bot={self._user.bot} nick={self.nick!r} guild={self.guild!r}>'
@ -444,6 +454,12 @@ class Member(discord.abc.Messageable, _BaseUser):
"""Union[:class:`BaseActivity`, :class:`Spotify`]: Returns the primary
activity the user is currently doing. Could be ``None`` if no activity is being done.
.. note::
Due to a Discord API limitation, this may be ``None`` if
the user is listening to a song on Spotify with a title longer
than 128 characters. See :issue:`1738` for more information.
.. note::
A user may have multiple activities, these can be accessed under :attr:`activities`.

View File

@ -131,6 +131,9 @@ class Attachment(Hashable):
""":class:`bool`: Whether this attachment contains a spoiler."""
return self.filename.startswith('SPOILER_')
def __int__(self):
return self.id
def __repr__(self):
return '<Attachment id={0.id} filename={0.filename!r} url={0.url!r}>'.format(self)
@ -339,6 +342,7 @@ class MessageReference:
self.message_id = utils._get_as_snowflake(data, 'message_id')
self.channel_id = int(data.pop('channel_id'))
self.guild_id = utils._get_as_snowflake(data, 'guild_id')
self.fail_if_not_exists = data.get('fail_if_not_exists', True)
self._state = state
self.resolved = None
return self
@ -368,6 +372,24 @@ class MessageReference:
self._state = message._state
return self
@classmethod
def from_message(cls, message):
"""Creates a :class:`MessageReference` from an existing :class:`Message`.
.. versionadded:: 1.5.1.5
Parameters
----------
message: :class:`Message`
The message to be converted into a reference.
Returns
-------
:class:`MessageReference`
A reference to the message.
"""
return cls(message._state, message_id=message.id, channel_id=message.channel.id, guild_id=message.guild and message.guild.id)
@property
def cached_message(self):
"""Optional[:class:`~discord.Message`]: The cached message, if found in the internal message cache."""
@ -579,6 +601,12 @@ class Message(Hashable):
except KeyError:
continue
def __str__(self):
return self.content
def __int__(self):
return self.id
def __repr__(self):
return '<Message id={0.id} channel={0.channel!r} type={0.type!r} author={0.author!r} flags={0.flags!r}>'.format(self)
@ -936,7 +964,7 @@ class Message(Hashable):
if self.type is MessageType.guild_discovery_grace_period_final_warning:
return 'This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery.'
async def delete(self, *, delay=None):
async def delete(self, *, delay=None, silent=False):
"""|coro|
Deletes the message.
@ -948,12 +976,17 @@ class Message(Hashable):
.. versionchanged:: 1.1
Added the new ``delay`` keyword-only parameter.
.. versionchanged:: 1.6.0.7
Added the new ``silent`` keyword-only parameter.
Parameters
-----------
delay: Optional[:class:`float`]
If provided, the number of seconds to wait in the background
before deleting the message. If the deletion fails then it is silently ignored.
silent: :class:`bool`
If silent is set to ``True``, the error will not be raised, it will be ignored.
This defaults to ``False``
Raises
------
Forbidden
@ -973,7 +1006,13 @@ class Message(Hashable):
asyncio.create_task(delete())
else:
await self._state.http.delete_message(self.channel.id, self.id)
try:
await self._state.http.delete_message(self.channel.id, self.id)
except Exception as e:
if silent:
pass
else:
raise e
async def edit(self, **fields):
"""|coro|
@ -1072,8 +1111,10 @@ class Message(Hashable):
Publishes this message to your announcement channel.
You must have the :attr:`~Permissions.send_messages` permission to do this.
If the message is not your own then the :attr:`~Permissions.manage_messages`
permission is needed.
permission is also needed.
Raises
-------
@ -1262,8 +1303,8 @@ class Message(Hashable):
async def reply(self, content=None, **kwargs):
"""|coro|
A shortcut method to :meth:`abc.Messageable.send` to reply to the
:class:`Message`.
A shortcut method to :meth:`.abc.Messageable.send` to reply to the
:class:`.Message`.
.. versionadded:: 1.6
@ -1279,7 +1320,7 @@ class Message(Hashable):
Returns
---------
:class:`Message`
:class:`.Message`
The message that was sent.
"""

View File

@ -283,6 +283,8 @@ class Permissions(BaseFlags):
"""
return 1 << 3
admin = administrator
@flag_value
def manage_channels(self):
""":class:`bool`: Returns ``True`` if a user can edit, delete, or create channels in the guild.

View File

@ -150,6 +150,9 @@ class Role(Hashable):
def __str__(self):
return self.name
def __int__(self):
return self.id
def __repr__(self):
return '<Role id={0.id} name={0.name!r}>'.format(self)

View File

@ -801,6 +801,9 @@ class ConnectionState:
return self._add_guild_from_data(data)
def is_guild_evicted(self, guild) -> bool:
return guild.id not in self._guilds
async def chunk_guild(self, guild, *, wait=True, cache=None):
cache = cache or self.member_cache_flags.joined
request = self._chunk_requests.get(guild.id)

View File

@ -32,7 +32,7 @@ __all__ = (
)
class Sticker(Hashable):
"""Represents a sticker
"""Represents a sticker.
.. versionadded:: 1.6
@ -40,34 +40,34 @@ class Sticker(Hashable):
.. describe:: str(x)
Returns the name of the sticker
Returns the name of the sticker.
.. describe:: x == y
Checks if the sticker is equal to another sticker
Checks if the sticker is equal to another sticker.
.. describe:: x != y
Checks if the sticker is not equal to another sticker
Checks if the sticker is not equal to another sticker.
Attributes
----------
name: :class:`str`
The sticker's name
The sticker's name.
id: :class:`int`
The id of the sticker
The id of the sticker.
description: :class:`str`
The description of the sticker
The description of the sticker.
pack_id: :class:`int`
The id of the sticker's pack
The id of the sticker's pack.
format: :class:`StickerType`
The format for the sticker's image
The format for the sticker's image.
image: :class:`str`
The sticker's image
The sticker's image.
tags: List[:class:`str`]
A list of tags for the sticker
A list of tags for the sticker.
preview_image: Optional[:class:`str`]
The sticker's preview asset hash
The sticker's preview asset hash.
"""
__slots__ = ('_state', 'id', 'name', 'description', 'pack_id', 'format', 'image', 'tags', 'preview_image')
@ -76,7 +76,7 @@ class Sticker(Hashable):
self.id = int(data['id'])
self.name = data['name']
self.description = data['description']
self.pack_id = int(data['pack_id'])
self.pack_id = int(data.get('pack_id', 0))
self.format = try_enum(StickerType, data['format_type'])
self.image = data['asset']
@ -103,7 +103,7 @@ class Sticker(Hashable):
"""Returns an :class:`Asset` for the sticker's image.
.. note::
This will return ``None`` if the format is ``StickerType.lottie``
This will return ``None`` if the format is ``StickerType.lottie``.
Returns
-------

View File

@ -46,6 +46,9 @@ class BaseUser(_BaseUser):
def __str__(self):
return '{0.name}#{0.discriminator}'.format(self)
def __int__(self):
return self.id
def __eq__(self, other):
return isinstance(other, _BaseUser) and other.id == self.id
@ -94,15 +97,12 @@ class BaseUser(_BaseUser):
@property
def avatar_url(self):
""":class:`Asset`: Returns an :class:`Asset` for the avatar the user has.
""":class:`str`: Returns an direct url for the avatar the user has.
If the user does not have a traditional avatar, an asset for
the default avatar is returned instead.
This is equivalent to calling :meth:`avatar_url_as` with
the default parameters (i.e. webp/gif detection and a size of 1024).
"""
return self.avatar_url_as(format=None, size=1024)
return str(self.avatar_url_as(static_format="png", size=1024))
def is_avatar_animated(self):
""":class:`bool`: Indicates if the user has an animated avatar."""
@ -269,7 +269,16 @@ class ClientUser(BaseUser):
.. versionadded:: 1.3
verified: :class:`bool`
<<<<<<< HEAD
Specifies if the user's email is verified.
email: Optional[:class:`str`]
The email the user used when registering.
.. deprecated:: 1.7
=======
Specifies if the user is a verified account.
>>>>>>> 523e35e4f3c3c49d4e471359f9fb559242bbecc8
locale: Optional[:class:`str`]
The IETF language tag used to identify the language the user is using.
mfa_enabled: :class:`bool`

View File

@ -73,7 +73,7 @@ class VoiceProtocol:
These classes are passed to :meth:`abc.Connectable.connect`.
.. _Lavalink: https://github.com/Frederikam/Lavalink
.. _Lavalink: https://github.com/freyacodes/Lavalink
Parameters
------------

View File

@ -597,7 +597,7 @@ class WebhookMessage(Message):
"""
if delay is not None:
if self._state.parent._adapter.is_async():
if self._state._webhook._adapter.is_async():
return self._delete_delay_async(delay)
else:
return self._delete_delay_sync(delay)

View File

@ -95,3 +95,9 @@ document.addEventListener('keydown', (event) => {
activeModal.close();
}
});
document.addEventListener('keydown', (event) => {
if (event.code == "Escape" && activeModal) {
activeModal.close();
}
});

View File

@ -271,6 +271,12 @@ header > nav.mobile-only {
header > nav.mobile-only .search {
width: 100%;
position: absolute;
top: 0;
right: 0;
z-index: -1;
padding-top: 0;
transition: top 0.5s ease-in-out;
}
header > nav.mobile-only .search-wrapper {

View File

@ -267,7 +267,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
If you want exception to propagate out of the :class:`Client` class
you can define an ``on_error`` handler consisting of a single empty
:ref:`py:raise`. Exceptions raised by ``on_error`` will not be
:ref:`raise statement <py:raise>`. Exceptions raised by ``on_error`` will not be
handled in any way by :class:`Client`.
.. note::
@ -834,7 +834,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
:type member: :class:`Member`
:param before: The voice state prior to the changes.
:type before: :class:`VoiceState`
:param after: The voice state after to the changes.
:param after: The voice state after the changes.
:type after: :class:`VoiceState`
.. function:: on_member_ban(guild, user)
@ -2791,6 +2791,8 @@ Role
RoleTags
~~~~~~~~~~
.. attributetable:: RoleTags
.. autoclass:: RoleTags()
:members:
@ -3051,24 +3053,32 @@ AllowedMentions
MessageReference
~~~~~~~~~~~~~~~~~
.. attributetable:: MessageReference
.. autoclass:: MessageReference
:members:
PartialMessage
~~~~~~~~~~~~~~~~~
.. attributetable:: PartialMessage
.. autoclass:: PartialMessage
:members:
Intents
~~~~~~~~~~
.. attributetable:: Intents
.. autoclass:: Intents
:members:
MemberCacheFlags
~~~~~~~~~~~~~~~~~~
.. attributetable:: MemberCacheFlags
.. autoclass:: MemberCacheFlags
:members:
@ -3147,24 +3157,32 @@ PermissionOverwrite
ShardInfo
~~~~~~~~~~~
.. attributetable:: ShardInfo
.. autoclass:: ShardInfo()
:members:
SystemChannelFlags
~~~~~~~~~~~~~~~~~~~~
.. attributetable:: SystemChannelFlags
.. autoclass:: SystemChannelFlags()
:members:
MessageFlags
~~~~~~~~~~~~
.. attributetable:: MessageFlags
.. autoclass:: MessageFlags()
:members:
PublicUserFlags
~~~~~~~~~~~~~~~
.. attributetable:: PublicUserFlags
.. autoclass:: PublicUserFlags()
:members:

View File

@ -348,4 +348,4 @@ def setup(app):
if app.config.language == 'ja':
app.config.intersphinx_mapping['py'] = ('https://docs.python.org/ja/3', None)
app.config.html_context['discord_invite'] = 'https://discord.gg/nXzj3dg'
app.config.resource_links['discord'] = 'https://discord.gg/nXzj3dg'
app.config.resource_links['discord'] = 'https://discord.gg/nXzj3dg'

34
docs/custom_features.rst Normal file
View File

@ -0,0 +1,34 @@
.. currentmodule:: discord
.. _custom_features:
Intro
=====
enhanced-dpy was made to add some extra features that make it just a bit easier. This custom version of discord.py will always remain up-to-date with the beta version, so not everything might work or be stable.
Custom Features
---------------
Here are the custom features listed that have been added to enhanced-dpy. You can refer to the changelog to see in what version they were added!
- **Documentation URL:** https://enhanced-dpy.readthedocs.io/en/latest/index.html
- Added :attr:`Guild.bots` and :attr:`Guild.humans`
- Added :attr:`Client.owner` and :attr:`Client.owners`
- Added :meth:`Client.try_user`
- :attr:`Guild.icon_url` and :attr:`User.avatar_url` return the string in stead of Asset. use icon/avatar url_as to get the :class:`Asset`
- Merged in ext-colors (https://github.com/MGardne8/DiscordPyColours)
- Using Rapptz/discord.py/tree/neo-docs for documentation
- Added support for ``hex()`` to :class:`Color`
- Added :attr:`Client.embed_color`
- Added :meth:`Client.set_embed_color`
- Added :attr:`TextChannel.can_send`
- Added :meth:`Intents.from_list`
- Added support for ``int()`` to :class:`User`, :class:`Member`, :class:`Emoji`, :class:`Role`, :class:`Guild`, :class:`Message`, :class:`TextChannel`, :class:`VoiceChannel`, :class:`CategoryChannel`, :class:`Attachment` and :class:`Message`. This will return their id
- Added support for ``str()`` to :class:`Message`. This will return the message content
- Added :meth:`Guild.try_member`
- Added :attr:`Context.clean_prefix <.ext.commands.Context.clean_prefix>`
- Added :meth:`Colour.nitro_booster`
- Added :attr:`Permissions.admin` as alias to :attr:`Permissions.administrator`
- Added :attr:`CogMeta.aliases <.ext.commands.CogMeta.aliases>`
- Added :attr:`Bot.case_insensitive_prefix <.ext.commands.Bot.case_insensitive_prefix>`
- Added ``silent`` kwarg to :meth:`Message.delete`
- Added :meth:`Client.get_message`

View File

@ -176,7 +176,8 @@ Paginator
Enums
------
.. class:: discord.ext.commands.BucketType
.. class:: BucketType
:module: discord.ext.commands
Specifies a type of bucket for, e.g. a cooldown.
@ -293,6 +294,9 @@ Converters
.. autoclass:: discord.ext.commands.StoreChannelConverter
:members:
.. autoclass:: discord.ext.commands.StageChannelConverter
:members:
.. autoclass:: discord.ext.commands.CategoryChannelConverter
:members:

View File

@ -327,7 +327,7 @@ For example, a common idiom would be to have a class and a converter for that cl
else:
await ctx.send("Hm you're not so new.")
This can get tedious, so an inline advanced converter is possible through a ``classmethod`` inside the type:
This can get tedious, so an inline advanced converter is possible through a :func:`classmethod` inside the type:
.. code-block:: python3
@ -379,6 +379,10 @@ A lot of discord models work out of the gate as a parameter:
- :class:`PartialMessage` (since v1.7)
- :class:`TextChannel`
- :class:`VoiceChannel`
<<<<<<< HEAD
- :class:`StageChannel` (since v1.7)
=======
>>>>>>> 523e35e4f3c3c49d4e471359f9fb559242bbecc8
- :class:`StoreChannel` (since v1.7)
- :class:`CategoryChannel`
- :class:`Invite`
@ -410,8 +414,15 @@ converter is given below:
+--------------------------+-------------------------------------------------+
| :class:`VoiceChannel` | :class:`~ext.commands.VoiceChannelConverter` |
+--------------------------+-------------------------------------------------+
<<<<<<< HEAD
| :class:`StageChannel` | :class:`~ext.commands.StageChannelConverter` |
+--------------------------+-------------------------------------------------+
| :class:`StoreChannel` | :class:`~ext.commands.StoreChannelConverter` |
+--------------------------+-------------------------------------------------+
=======
| :class:`StoreChannel` | :class:`~ext.commands.StoreChannelConverter` |
+--------------------------+-------------------------------------------------+
>>>>>>> 523e35e4f3c3c49d4e471359f9fb559242bbecc8
| :class:`CategoryChannel` | :class:`~ext.commands.CategoryChannelConverter` |
+--------------------------+-------------------------------------------------+
| :class:`Invite` | :class:`~ext.commands.InviteConverter` |

View File

@ -27,7 +27,7 @@ An example extension looks like this:
def setup(bot):
bot.add_command(hello)
In this example we define a simple command, and when the extension is loaded this command is added to the bot. Now the final step to this is loading the extension, which we do by calling :meth:`.commands.Bot.load_extension`. To load this extension we call ``bot.load_extension('hello')``.
In this example we define a simple command, and when the extension is loaded this command is added to the bot. Now the final step to this is loading the extension, which we do by calling :meth:`.Bot.load_extension`. To load this extension we call ``bot.load_extension('hello')``.
.. admonition:: Cogs
:class: helpful
@ -41,7 +41,7 @@ In this example we define a simple command, and when the extension is loaded thi
Reloading
-----------
When you make a change to the extension and want to reload the references, the library comes with a function to do this for you, :meth:`Bot.reload_extension`.
When you make a change to the extension and want to reload the references, the library comes with a function to do this for you, :meth:`.Bot.reload_extension`.
.. code-block:: python3

31
docs/ext/menus/index.rst Normal file
View File

@ -0,0 +1,31 @@
.. _discord_ext_menus:
``discord.ext.menus``
====================================================
Something something OOOOOOHH built in paginator!
Recipes
---------
Idk, some examples someday
.. _ext_menus_api:
API Reference
---------------
.. autoclass:: discord.ext.menus.Menu()
:members:
.. autoclass:: discord.ext.menus.PageSource()
:members:
.. autoclass:: discord.ext.menus.MenuPages()
:members:
.. autoclass:: discord.ext.menus.ListPageSource()
:members:
.. autofunction:: discord.ext.tasks.menus

View File

@ -234,7 +234,7 @@ technically in another thread, we must take caution in calling thread-safe opera
us, :mod:`asyncio` comes with a :func:`asyncio.run_coroutine_threadsafe` function that allows us to call
a coroutine from another thread.
However, this function returns a :class:`concurrent.Future` and to actually call it we have to fetch its result. Putting all of
However, this function returns a :class:`~concurrent.futures.Future` and to actually call it we have to fetch its result. Putting all of
this together we can do the following: ::
def my_after(error):
@ -295,7 +295,7 @@ How do I make a web request?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To make a request, you should use a non-blocking library.
This library already uses and requires a 3rd party library for making requests, ``aiohttp``.
This library already uses and requires a 3rd party library for making requests, :doc:`aiohttp <aio:index>`.
Quick example: ::
@ -393,7 +393,7 @@ Example: ::
How do I make a subcommand?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use the ``group`` decorator. This will transform the callback into a ``Group`` which will allow you to add commands into
Use the :func:`~ext.commands.group` decorator. This will transform the callback into a :class:`~ext.commands.Group` which will allow you to add commands into
the group operating as "subcommands". These groups can be arbitrarily nested as well.
Example: ::

View File

@ -11,8 +11,17 @@ Welcome to discord.py
discord.py is a modern, easy to use, feature-rich, and async ready API wrapper
for Discord.
Credits to the `original lib by Rapptz <https://github.com/iDutchy/discord.py>`_
**Features:**
**WARNING: This is not the official discord.py lib! This is a custom version to which I add some features that might be useful or just makes things easier for the lazy people. See below which features have been added. This lib will also be kept updated with the BETA version of the original lib! So things may be unstable, please keep that in mind.**
Custom Features
---------------
**Moved to:** `Custom Features <https://enhanced-dpy.readthedocs.io/en/latest/custom_features.html>`_
Features
--------
- Modern Pythonic API using ``async``\/``await`` syntax
- Sane rate limit handling that prevents 429s

View File

@ -52,8 +52,9 @@ There's a lot going on here, so let's walk you through it step by step.
4. Since the :func:`on_message` event triggers for *every* message received, we have to make
sure that we ignore messages from ourselves. We do this by checking if the :attr:`Message.author`
is the same as the :attr:`Client.user`.
5. Afterwards, we check if the :class:`Message.content` starts with ``'$hello'``. If it is,
then we send a message in the channel it was used in with ``'Hello!'``.
5. Afterwards, we check if the :class:`Message.content` starts with ``'$hello'``. If it does,
then we send a message in the channel it was used in with ``'Hello!'``. This is a basic way of
handling commands, which can be later automated with the :ref:`ext.commands` framework.
6. Finally, we run the bot with our login token. If you need help getting your token or creating a bot,
look in the :ref:`discord-intro` section.

View File

@ -11,6 +11,29 @@ Changelog
This page keeps a detailed human friendly rendering of what's new and changed
in specific versions.
.. _vp1p7p3:
v1.7.3
--------
Bug Fixes
~~~~~~~~~~
- Fix a crash involving guild uploaded stickers
- Fix :meth:`DMChannel.permissions_for` not having :attr:`Permissions.read_messages` set.
.. _vp1p7p2:
v1.7.2
-------
Bug Fixes
~~~~~~~~~~~
- Fix ``fail_if_not_exists`` causing certain message references to not be usable within :meth:`abc.Messageable.send` and :meth:`Message.reply` (:issue:`6726`)
- Fix :meth:`Guild.chunk` hanging when the user left the guild. (:issue:`6730`)
- Fix loop sleeping after final iteration rather than before (:issue:`6744`)
.. _vp1p7p1:
v1.7.1
@ -32,9 +55,10 @@ Likewise, **this is the last version to support user bots**.
Development of v2.0 will have breaking changes and support for newer API features.
New Features
~~~~~~~~~~~~~~
<<<<<<< HEAD
=======
>>>>>>> 523e35e4f3c3c49d4e471359f9fb559242bbecc8
- Add support for stage channels via :class:`StageChannel` (:issue:`6602`, :issue:`6608`)
- Add support for :attr:`MessageReference.fail_if_not_exists` (:issue:`6484`)
- By default, if the message you're replying to doesn't exist then the API errors out.
@ -104,6 +128,19 @@ Miscellaneous
- :class:`Permission` class methods were updated to match the UI of the Discord client (:issue:`6476`)
- ``_`` and ``-`` characters are now stripped when making a new cog using the ``discord`` package (:issue:`6313`)
.. _vp1p6p0p7:
v1.6.0.7
--------
New Features
~~~~~~~~~~~~~~
- Add :attr:`CogMeta.aliases <.ext.commands.CogMeta.aliases>`
- Add :attr:`Bot.case_insensitive_prefix <.ext.commands.Bot.case_insensitive_prefix>`
- Add ``silent`` kwargs to :meth:`Message.delete`
- Add :meth:`Client.get_message`
.. _vp1p6p0:
v1.6.0
@ -176,6 +213,49 @@ Miscellaneous
- |commands| :class:`UserConverter <ext.commands.UserConverter>` now fetches the API if an ID is passed and the user is not cached.
- |commands| :func:`max_concurrency <ext.commands.max_concurrency>` is now called before cooldowns (:issue:`6172`)
.. _vp1p5p1p6:
v1.5.1.6
--------
New Features
~~~~~~~~~~~~~~
- Add :meth:`Colour.random`
.. _vp1p5p1p5:
v1.5.1.5
--------
New Features
~~~~~~~~~~~~~~
- Add :meth:`Colour.nitro_booster`
- Add :attr:`Permissions.admin` as alias to :attr:`Permissions.administrator`
New Beta Features
~~~~~~~~~~~~~~~~~~~
These are all for message replies. I have added them to 1.5.1.5 but they will most likely officially get added in the original lib in 1.6 or 2.0
- |commands| Add :meth:`Context.reply <ext.commands.Context>`
- Add :meth:`Message.reply`
- Add ``replied_user`` to :class:`AllowedMentions`
- Add :meth:`MessageReference.to_dict`
- Add :meth:`MessageReference.from_message`
- Add ``message_reference`` kwarg to :meth:`abc.Messageable.send`
.. _vp1p5p1p4:
v1.5.1.4
--------
New Features
~~~~~~~~~~~~~~
- |commands| Add :attr:`Context.clean_prefix <ext.commands.Context>`
.. _vp1p5p1:
v1.5.1
@ -201,6 +281,52 @@ Miscellaneous
- This is the same as having ``discord.Member`` as the type-hint.
- :meth:`Guild.chunk` now allows concurrent calls without spamming the gateway with requests.
.. _v1p5p0p3:
v1.5.0.3
--------
Bug Fixes
~~~~~~~~~~~
- Fix :attr:`TextChannel.can_send` to use proper checks
.. _vp1p5p0p2:
v1.5.0.2
--------
New Features
~~~~~~~~~~~~~~
- Add :attr:`Guild.try_member`
- Add :attr:`TextChannel.can_send`
.. _vp1p5p0p1:
v1.5.0.1
--------
New Features
~~~~~~~~~~~~~~
- Changed :attr:`Guild.icon_url` and :attr:`User.avatar_url` to return a string of the url in stead of an :class:`Asset`
- Documentation uses `Neo Docs by Rapptz <Rapptz/discord.py/tree/neo-docs>`_
- Add 1000+ colors from `ext colors <https://github.com/MGardne8/DiscordPyColours>`_
- Add support for ``hex()`` to :class:`Colour`
- Add :attr:`Guild.bots`
- Add :attr:`Guild.humans`
- |commands| Add :attr:`Bot.owner <ext.commands.Bot>`
- |commands| Add :attr:`Bot.owners <ext.commands.Bot>`
- Add :attr:`Guild.humans`
- Add :meth:`Client.try_user`
- Add :attr:`Client.embed_color`
- Add :meth:`Client.set_embed_color`
- Add :meth:`Intents.from_list`
- Add ``str()`` support to :class:`Message` which will return the :attr:`Message.content`
- Add ``int()`` support to :class:`User`, :class:`Member`, :class:`Emoji`, :class:`Role`, :class:`Guild`, :class:`Message`, :class:`TextChannel`, :class:`VoiceChannel`, :class:`CategoryChannel`, :class:`Attachment` and :class:`Message`. This will return the ID of the object
.. _vp1p5p0:
v1.5.0

View File

@ -1 +1 @@
aiohttp>=3.6.0,<3.8.0
aiohttp>=3.6.0,<3.7.0

View File

@ -42,12 +42,12 @@ extras_require = {
]
}
setup(name='discord.py',
author='Rapptz',
url='https://github.com/Rapptz/discord.py',
setup(name='enhanced-dpy',
author='iDutchy',
url='https://github.com/iDutchy/discord.py',
project_urls={
"Documentation": "https://discordpy.readthedocs.io/en/latest/",
"Issue tracker": "https://github.com/Rapptz/discord.py/issues",
"Documentation": "https://enhanced-dpy.readthedocs.io/en/latest/",
"Issue tracker": "https://github.com/iDutchy/discord.py/issues",
},
version=version,
packages=['discord', 'discord.types', 'discord.ext.commands', 'discord.ext.tasks'],