Add support for voice channel parties #33
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,3 +14,5 @@ docs/crowdin.py
|
||||
*.jpg
|
||||
*.flac
|
||||
*.mo
|
||||
dist
|
||||
build
|
41
README.rst
41
README.rst
@ -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>`_
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
-----------
|
||||
|
5296
discord/colour.py
5296
discord/colour.py
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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__', ''))
|
||||
|
@ -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|
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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
|
||||
|
1202
discord/ext/menus/__init__.py
Normal file
1202
discord/ext/menus/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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|
|
||||
|
@ -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):
|
||||
|
@ -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`.
|
||||
|
@ -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.
|
||||
"""
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
-------
|
||||
|
@ -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`
|
||||
|
@ -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
|
||||
------------
|
||||
|
@ -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)
|
||||
|
6
docs/_static/custom.js
vendored
6
docs/_static/custom.js
vendored
@ -95,3 +95,9 @@ document.addEventListener('keydown', (event) => {
|
||||
activeModal.close();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.code == "Escape" && activeModal) {
|
||||
activeModal.close();
|
||||
}
|
||||
});
|
||||
|
6
docs/_static/style.css
vendored
6
docs/_static/style.css
vendored
@ -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 {
|
||||
|
22
docs/api.rst
22
docs/api.rst
@ -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:
|
||||
|
||||
|
@ -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
34
docs/custom_features.rst
Normal 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`
|
@ -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:
|
||||
|
||||
|
@ -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` |
|
||||
|
@ -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
31
docs/ext/menus/index.rst
Normal 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
|
@ -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: ::
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
aiohttp>=3.6.0,<3.8.0
|
||||
aiohttp>=3.6.0,<3.7.0
|
||||
|
10
setup.py
10
setup.py
@ -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'],
|
||||
|
Loading…
x
Reference in New Issue
Block a user