fix merge conflict
This commit is contained in:
commit
6e024871ec
@ -15,7 +15,7 @@ __title__ = 'discord'
|
|||||||
__author__ = 'Rapptz'
|
__author__ = 'Rapptz'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright 2015-2020 Rapptz'
|
__copyright__ = 'Copyright 2015-2020 Rapptz'
|
||||||
__version__ = '1.5.1.6'
|
__version__ = '1.6.0.6a'
|
||||||
|
|
||||||
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
||||||
|
|
||||||
@ -58,10 +58,11 @@ from .voice_client import VoiceClient, VoiceProtocol
|
|||||||
from .audit_logs import AuditLogChanges, AuditLogEntry, AuditLogDiff
|
from .audit_logs import AuditLogChanges, AuditLogEntry, AuditLogDiff
|
||||||
from .raw_models import *
|
from .raw_models import *
|
||||||
from .team import *
|
from .team import *
|
||||||
|
from .sticker import Sticker
|
||||||
|
|
||||||
VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
|
VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
|
||||||
|
|
||||||
version_info = VersionInfo(major=1, minor=5, micro=1, releaselevel='final', serial=0)
|
version_info = VersionInfo(major=1, minor=6, micro=0, releaselevel='alpha', serial=0)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from logging import NullHandler
|
from logging import NullHandler
|
||||||
|
@ -713,7 +713,7 @@ class GuildChannel:
|
|||||||
async def create_invite(self, *, reason=None, **fields):
|
async def create_invite(self, *, reason=None, **fields):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Creates an instant invite.
|
Creates an instant invite from a text or voice channel.
|
||||||
|
|
||||||
You must have the :attr:`~Permissions.create_instant_invite` permission to
|
You must have the :attr:`~Permissions.create_instant_invite` permission to
|
||||||
do this.
|
do this.
|
||||||
@ -741,6 +741,9 @@ class GuildChannel:
|
|||||||
~discord.HTTPException
|
~discord.HTTPException
|
||||||
Invite creation failed.
|
Invite creation failed.
|
||||||
|
|
||||||
|
~discord.NotFound
|
||||||
|
The channel that was passed is a category or an invalid channel.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
--------
|
--------
|
||||||
:class:`~discord.Invite`
|
:class:`~discord.Invite`
|
||||||
|
@ -131,17 +131,79 @@ class AppInfo:
|
|||||||
def icon_url(self):
|
def icon_url(self):
|
||||||
""":class:`.Asset`: Retrieves the application's icon asset.
|
""":class:`.Asset`: Retrieves the application's icon asset.
|
||||||
|
|
||||||
|
This is equivalent to calling :meth:`icon_url_as` with
|
||||||
|
the default parameters ('webp' format and a size of 1024).
|
||||||
|
|
||||||
.. versionadded:: 1.3
|
.. versionadded:: 1.3
|
||||||
"""
|
"""
|
||||||
return Asset._from_icon(self._state, self, 'app')
|
return self.icon_url_as()
|
||||||
|
|
||||||
|
def icon_url_as(self, *, format='webp', size=1024):
|
||||||
|
"""Returns an :class:`Asset` for the icon the application has.
|
||||||
|
|
||||||
|
The format must be one of 'webp', 'jpeg', 'jpg' or 'png'.
|
||||||
|
The size must be a power of 2 between 16 and 4096.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
format: :class:`str`
|
||||||
|
The format to attempt to convert the icon to. Defaults to 'webp'.
|
||||||
|
size: :class:`int`
|
||||||
|
The size of the image to display.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
InvalidArgument
|
||||||
|
Bad image format passed to ``format`` or invalid ``size``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`Asset`
|
||||||
|
The resulting CDN asset.
|
||||||
|
"""
|
||||||
|
return Asset._from_icon(self._state, self, 'app', format=format, size=size)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cover_image_url(self):
|
def cover_image_url(self):
|
||||||
""":class:`.Asset`: Retrieves the cover image on a store embed.
|
""":class:`.Asset`: Retrieves the cover image on a store embed.
|
||||||
|
|
||||||
|
This is equivalent to calling :meth:`cover_image_url_as` with
|
||||||
|
the default parameters ('webp' format and a size of 1024).
|
||||||
|
|
||||||
.. versionadded:: 1.3
|
.. versionadded:: 1.3
|
||||||
"""
|
"""
|
||||||
return Asset._from_cover_image(self._state, self)
|
return self.cover_image_url_as()
|
||||||
|
|
||||||
|
def cover_image_url_as(self, *, format='webp', size=1024):
|
||||||
|
"""Returns an :class:`Asset` for the image on store embeds
|
||||||
|
if this application is a game sold on Discord.
|
||||||
|
|
||||||
|
The format must be one of 'webp', 'jpeg', 'jpg' or 'png'.
|
||||||
|
The size must be a power of 2 between 16 and 4096.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
format: :class:`str`
|
||||||
|
The format to attempt to convert the image to. Defaults to 'webp'.
|
||||||
|
size: :class:`int`
|
||||||
|
The size of the image to display.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
InvalidArgument
|
||||||
|
Bad image format passed to ``format`` or invalid ``size``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`Asset`
|
||||||
|
The resulting CDN asset.
|
||||||
|
"""
|
||||||
|
return Asset._from_cover_image(self._state, self, format=format, size=size)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def guild(self):
|
def guild(self):
|
||||||
|
@ -89,19 +89,29 @@ class Asset:
|
|||||||
return cls(state, '/avatars/{0.id}/{0.avatar}.{1}?size={2}'.format(user, format, size))
|
return cls(state, '/avatars/{0.id}/{0.avatar}.{1}?size={2}'.format(user, format, size))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_icon(cls, state, object, path):
|
def _from_icon(cls, state, object, path, *, format='webp', size=1024):
|
||||||
if object.icon is None:
|
if object.icon is None:
|
||||||
return cls(state)
|
return cls(state)
|
||||||
|
|
||||||
url = '/{0}-icons/{1.id}/{1.icon}.jpg'.format(path, object)
|
if not utils.valid_icon_size(size):
|
||||||
|
raise InvalidArgument("size must be a power of 2 between 16 and 4096")
|
||||||
|
if format not in VALID_STATIC_FORMATS:
|
||||||
|
raise InvalidArgument("format must be None or one of {}".format(VALID_STATIC_FORMATS))
|
||||||
|
|
||||||
|
url = '/{0}-icons/{1.id}/{1.icon}.{2}?size={3}'.format(path, object, format, size)
|
||||||
return cls(state, url)
|
return cls(state, url)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_cover_image(cls, state, obj):
|
def _from_cover_image(cls, state, obj, *, format='webp', size=1024):
|
||||||
if obj.cover_image is None:
|
if obj.cover_image is None:
|
||||||
return cls(state)
|
return cls(state)
|
||||||
|
|
||||||
url = '/app-assets/{0.id}/store/{0.cover_image}.jpg'.format(obj)
|
if not utils.valid_icon_size(size):
|
||||||
|
raise InvalidArgument("size must be a power of 2 between 16 and 4096")
|
||||||
|
if format not in VALID_STATIC_FORMATS:
|
||||||
|
raise InvalidArgument("format must be None or one of {}".format(VALID_STATIC_FORMATS))
|
||||||
|
|
||||||
|
url = '/app-assets/{0.id}/store/{0.cover_image}.{1}?size={2}'.format(obj, format, size)
|
||||||
return cls(state, url)
|
return cls(state, url)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -136,6 +146,12 @@ class Asset:
|
|||||||
|
|
||||||
return cls(state, '/icons/{0.id}/{0.icon}.{1}?size={2}'.format(guild, format, size))
|
return cls(state, '/icons/{0.id}/{0.icon}.{1}?size={2}'.format(guild, format, size))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _from_sticker_url(cls, state, sticker, *, size=1024):
|
||||||
|
if not utils.valid_icon_size(size):
|
||||||
|
raise InvalidArgument("size must be a power of 2 between 16 and 4096")
|
||||||
|
|
||||||
|
return cls(state, '/stickers/{0.id}/{0.image}.png?size={2}'.format(sticker, format, size))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.BASE + self._url if self._url is not None else ''
|
return self.BASE + self._url if self._url is not None else ''
|
||||||
|
@ -1161,8 +1161,39 @@ class GroupChannel(discord.abc.Messageable, Hashable):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def icon_url(self):
|
def icon_url(self):
|
||||||
""":class:`Asset`: Returns the channel's icon asset if available."""
|
""":class:`Asset`: Returns the channel's icon asset if available.
|
||||||
return Asset._from_icon(self._state, self, 'channel')
|
|
||||||
|
This is equivalent to calling :meth:`icon_url_as` with
|
||||||
|
the default parameters ('webp' format and a size of 1024).
|
||||||
|
"""
|
||||||
|
return self.icon_url_as()
|
||||||
|
|
||||||
|
def icon_url_as(self, *, format='webp', size=1024):
|
||||||
|
"""Returns an :class:`Asset` for the icon the channel has.
|
||||||
|
|
||||||
|
The format must be one of 'webp', 'jpeg', 'jpg' or 'png'.
|
||||||
|
The size must be a power of 2 between 16 and 4096.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
format: :class:`str`
|
||||||
|
The format to attempt to convert the icon to. Defaults to 'webp'.
|
||||||
|
size: :class:`int`
|
||||||
|
The size of the image to display.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
InvalidArgument
|
||||||
|
Bad image format passed to ``format`` or invalid ``size``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`Asset`
|
||||||
|
The resulting CDN asset.
|
||||||
|
"""
|
||||||
|
return Asset._from_icon(self._state, self, 'channel', format=format, size=size)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at(self):
|
def created_at(self):
|
||||||
|
@ -129,6 +129,19 @@ class Colour:
|
|||||||
"""A factory method that returns a :class:`Colour` with a value of ``0``."""
|
"""A factory method that returns a :class:`Colour` with a value of ``0``."""
|
||||||
return cls(0)
|
return cls(0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def random(cls):
|
||||||
|
"""A factory method that returns a :class:`Colour` with a random hue.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The random algorithm works by choosing a colour with a random hue but
|
||||||
|
with maxed out saturation and value.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
"""
|
||||||
|
return cls.from_hsv(random.random(), 1, 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def teal(cls):
|
def teal(cls):
|
||||||
"""A factory method that returns a :class:`Colour` with a value of ``0x1abc9c``."""
|
"""A factory method that returns a :class:`Colour` with a value of ``0x1abc9c``."""
|
||||||
|
@ -52,6 +52,7 @@ __all__ = (
|
|||||||
'WebhookType',
|
'WebhookType',
|
||||||
'ExpireBehaviour',
|
'ExpireBehaviour',
|
||||||
'ExpireBehavior',
|
'ExpireBehavior',
|
||||||
|
'StickerType',
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_value_cls(name):
|
def _create_value_cls(name):
|
||||||
@ -455,3 +456,8 @@ def try_enum(cls, val):
|
|||||||
return cls._enum_value_map_[val]
|
return cls._enum_value_map_[val]
|
||||||
except (KeyError, TypeError, AttributeError):
|
except (KeyError, TypeError, AttributeError):
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
class StickerType(Enum):
|
||||||
|
png = 1
|
||||||
|
apng = 2
|
||||||
|
lottie = 3
|
||||||
|
@ -129,7 +129,7 @@ class MemberConverter(IDConverter):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def query_member_named(self, guild, argument):
|
async def query_member_named(self, guild, argument):
|
||||||
cache = guild._state._member_cache_flags.joined
|
cache = guild._state.member_cache_flags.joined
|
||||||
if len(argument) > 5 and argument[-5] == '#':
|
if len(argument) > 5 and argument[-5] == '#':
|
||||||
username, _, discriminator = argument.rpartition('#')
|
username, _, discriminator = argument.rpartition('#')
|
||||||
members = await guild.query_members(username, limit=100, cache=cache)
|
members = await guild.query_members(username, limit=100, cache=cache)
|
||||||
@ -140,7 +140,7 @@ class MemberConverter(IDConverter):
|
|||||||
|
|
||||||
async def query_member_by_id(self, bot, guild, user_id):
|
async def query_member_by_id(self, bot, guild, user_id):
|
||||||
ws = bot._get_websocket(shard_id=guild.shard_id)
|
ws = bot._get_websocket(shard_id=guild.shard_id)
|
||||||
cache = guild._state._member_cache_flags.joined
|
cache = guild._state.member_cache_flags.joined
|
||||||
if ws.is_ratelimited():
|
if ws.is_ratelimited():
|
||||||
# If we're being rate limited on the WS, then fall back to using the HTTP API
|
# 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
|
# So we don't have to wait ~60 seconds for the query to finish
|
||||||
@ -206,6 +206,10 @@ class UserConverter(IDConverter):
|
|||||||
|
|
||||||
.. versionchanged:: 1.5
|
.. versionchanged:: 1.5
|
||||||
Raise :exc:`.UserNotFound` instead of generic :exc:`.BadArgument`
|
Raise :exc:`.UserNotFound` instead of generic :exc:`.BadArgument`
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
This converter now lazily fetches users from the HTTP APIs if an ID is passed
|
||||||
|
and it's not available in cache.
|
||||||
"""
|
"""
|
||||||
async def convert(self, ctx, argument):
|
async def convert(self, ctx, argument):
|
||||||
match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument)
|
match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument)
|
||||||
@ -214,8 +218,15 @@ class UserConverter(IDConverter):
|
|||||||
|
|
||||||
if match is not None:
|
if match is not None:
|
||||||
user_id = int(match.group(1))
|
user_id = int(match.group(1))
|
||||||
result = await ctx.bot.try_user(user_id) or _utils_get(ctx.message.mentions, id=user_id)
|
result = ctx.bot.get_user(user_id) or _utils_get(ctx.message.mentions, id=user_id)
|
||||||
else:
|
if result is None:
|
||||||
|
try:
|
||||||
|
result = await ctx.bot.try_user(user_id)
|
||||||
|
except discord.HTTPException:
|
||||||
|
raise UserNotFound(argument) from None
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
arg = argument
|
arg = argument
|
||||||
|
|
||||||
# Remove the '@' character if this is the first character from the argument
|
# Remove the '@' character if this is the first character from the argument
|
||||||
@ -254,12 +265,11 @@ class MessageConverter(Converter):
|
|||||||
.. versionchanged:: 1.5
|
.. versionchanged:: 1.5
|
||||||
Raise :exc:`.ChannelNotFound`, `MessageNotFound` or `ChannelNotReadable` instead of generic :exc:`.BadArgument`
|
Raise :exc:`.ChannelNotFound`, `MessageNotFound` or `ChannelNotReadable` instead of generic :exc:`.BadArgument`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx, argument):
|
async def convert(self, ctx, argument):
|
||||||
id_regex = re.compile(r'^(?:(?P<channel_id>[0-9]{15,21})-)?(?P<message_id>[0-9]{15,21})$')
|
id_regex = re.compile(r'(?:(?P<channel_id>[0-9]{15,21})-)?(?P<message_id>[0-9]{15,21})$')
|
||||||
link_regex = re.compile(
|
link_regex = re.compile(
|
||||||
r'^https?://(?:(ptb|canary)\.)?discord(?:app)?\.com/channels/'
|
r'https?://(?:(ptb|canary|www)\.)?discord(?:app)?\.com/channels/'
|
||||||
r'(?:([0-9]{15,21})|(@me))'
|
r'(?:[0-9]{15,21}|@me)'
|
||||||
r'/(?P<channel_id>[0-9]{15,21})/(?P<message_id>[0-9]{15,21})/?$'
|
r'/(?P<channel_id>[0-9]{15,21})/(?P<message_id>[0-9]{15,21})/?$'
|
||||||
)
|
)
|
||||||
match = id_regex.match(argument) or link_regex.match(argument)
|
match = id_regex.match(argument) or link_regex.match(argument)
|
||||||
|
@ -868,6 +868,10 @@ class MemberCacheFlags(BaseFlags):
|
|||||||
self.value = self.DEFAULT_VALUE
|
self.value = self.DEFAULT_VALUE
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _empty(self):
|
||||||
|
return self.value == self.DEFAULT_VALUE
|
||||||
|
|
||||||
@flag_value
|
@flag_value
|
||||||
def online(self):
|
def online(self):
|
||||||
""":class:`bool`: Whether to cache members with a status.
|
""":class:`bool`: Whether to cache members with a status.
|
||||||
|
@ -308,8 +308,8 @@ class Guild(Hashable):
|
|||||||
self._rules_channel_id = utils._get_as_snowflake(guild, 'rules_channel_id')
|
self._rules_channel_id = utils._get_as_snowflake(guild, 'rules_channel_id')
|
||||||
self._public_updates_channel_id = utils._get_as_snowflake(guild, 'public_updates_channel_id')
|
self._public_updates_channel_id = utils._get_as_snowflake(guild, 'public_updates_channel_id')
|
||||||
|
|
||||||
cache_online_members = self._state._member_cache_flags.online
|
cache_online_members = self._state.member_cache_flags.online
|
||||||
cache_joined = self._state._member_cache_flags.joined
|
cache_joined = self._state.member_cache_flags.joined
|
||||||
self_id = self._state.self_id
|
self_id = self._state.self_id
|
||||||
for mdata in guild.get('members', []):
|
for mdata in guild.get('members', []):
|
||||||
member = Member(data=mdata, guild=self, state=state)
|
member = Member(data=mdata, guild=self, state=state)
|
||||||
@ -1253,7 +1253,8 @@ class Guild(Hashable):
|
|||||||
def fetch_members(self, *, limit=1000, after=None):
|
def fetch_members(self, *, limit=1000, after=None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Retrieves an :class:`.AsyncIterator` that enables receiving the guild's members.
|
Retrieves an :class:`.AsyncIterator` that enables receiving the guild's members. In order to use this,
|
||||||
|
:meth:`Intents.members` must be enabled.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -1274,6 +1275,8 @@ class Guild(Hashable):
|
|||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
|
ClientException
|
||||||
|
The members intent is not enabled.
|
||||||
HTTPException
|
HTTPException
|
||||||
Getting the members failed.
|
Getting the members failed.
|
||||||
|
|
||||||
@ -1295,6 +1298,10 @@ class Guild(Hashable):
|
|||||||
members = await guild.fetch_members(limit=150).flatten()
|
members = await guild.fetch_members(limit=150).flatten()
|
||||||
# members is now a list of Member...
|
# members is now a list of Member...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not self._state._intents.members:
|
||||||
|
raise ClientException('Intents.members must be enabled to use this.')
|
||||||
|
|
||||||
return MemberIterator(self, limit=limit, after=after)
|
return MemberIterator(self, limit=limit, after=after)
|
||||||
|
|
||||||
async def try_member(self, member_id):
|
async def try_member(self, member_id):
|
||||||
|
@ -241,6 +241,8 @@ class HTTPClient:
|
|||||||
raise Forbidden(r, data)
|
raise Forbidden(r, data)
|
||||||
elif r.status == 404:
|
elif r.status == 404:
|
||||||
raise NotFound(r, data)
|
raise NotFound(r, data)
|
||||||
|
elif r.status == 503:
|
||||||
|
raise DiscordServerError(r, data)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(r, data)
|
raise HTTPException(r, data)
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ from .file import File
|
|||||||
from .utils import escape_mentions
|
from .utils import escape_mentions
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .mixins import Hashable
|
from .mixins import Hashable
|
||||||
|
from .sticker import Sticker
|
||||||
|
|
||||||
|
|
||||||
class Attachment:
|
class Attachment:
|
||||||
@ -286,11 +287,15 @@ class MessageReference:
|
|||||||
|
|
||||||
def flatten_handlers(cls):
|
def flatten_handlers(cls):
|
||||||
prefix = len('_handle_')
|
prefix = len('_handle_')
|
||||||
cls._HANDLERS = {
|
handlers = [
|
||||||
key[prefix:]: value
|
(key[prefix:], value)
|
||||||
for key, value in cls.__dict__.items()
|
for key, value in cls.__dict__.items()
|
||||||
if key.startswith('_handle_')
|
if key.startswith('_handle_') and key != '_handle_member'
|
||||||
}
|
]
|
||||||
|
|
||||||
|
# store _handle_member last
|
||||||
|
handlers.append(('member', cls._handle_member))
|
||||||
|
cls._HANDLERS = handlers
|
||||||
cls._CACHED_SLOTS = [
|
cls._CACHED_SLOTS = [
|
||||||
attr for attr in cls.__slots__ if attr.startswith('_cs_')
|
attr for attr in cls.__slots__ if attr.startswith('_cs_')
|
||||||
]
|
]
|
||||||
@ -392,6 +397,10 @@ class Message(Hashable):
|
|||||||
- ``description``: A string representing the application's description.
|
- ``description``: A string representing the application's description.
|
||||||
- ``icon``: A string representing the icon ID of the application.
|
- ``icon``: A string representing the icon ID of the application.
|
||||||
- ``cover_image``: A string representing the embed's image asset ID.
|
- ``cover_image``: A string representing the embed's image asset ID.
|
||||||
|
stickers: List[:class:`Sticker`]
|
||||||
|
A list of stickers given to the message.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
|
__slots__ = ('_edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
|
||||||
@ -400,7 +409,7 @@ class Message(Hashable):
|
|||||||
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
||||||
'role_mentions', '_cs_raw_role_mentions', 'type', 'call', 'flags',
|
'role_mentions', '_cs_raw_role_mentions', 'type', 'call', 'flags',
|
||||||
'_cs_system_content', '_cs_guild', '_state', 'reactions', 'reference',
|
'_cs_system_content', '_cs_guild', '_state', 'reactions', 'reference',
|
||||||
'application', 'activity')
|
'application', 'activity', 'stickers')
|
||||||
|
|
||||||
def __init__(self, *, state, channel, data):
|
def __init__(self, *, state, channel, data):
|
||||||
self._state = state
|
self._state = state
|
||||||
@ -420,6 +429,7 @@ class Message(Hashable):
|
|||||||
self.tts = data['tts']
|
self.tts = data['tts']
|
||||||
self.content = data['content']
|
self.content = data['content']
|
||||||
self.nonce = data.get('nonce')
|
self.nonce = data.get('nonce')
|
||||||
|
self.stickers = [Sticker(data=data, state=state) for data in data.get('stickers', [])]
|
||||||
|
|
||||||
ref = data.get('message_reference')
|
ref = data.get('message_reference')
|
||||||
self.reference = MessageReference(state, **ref) if ref is not None else None
|
self.reference = MessageReference(state, **ref) if ref is not None else None
|
||||||
@ -496,10 +506,13 @@ class Message(Hashable):
|
|||||||
return reaction
|
return reaction
|
||||||
|
|
||||||
def _update(self, data):
|
def _update(self, data):
|
||||||
handlers = self._HANDLERS
|
# In an update scheme, 'author' key has to be handled before 'member'
|
||||||
for key, value in data.items():
|
# otherwise they overwrite each other which is undesirable.
|
||||||
|
# Since there's no good way to do this we have to iterate over every
|
||||||
|
# handler rather than iterating over the keys which is a little slower
|
||||||
|
for key, handler in self._HANDLERS:
|
||||||
try:
|
try:
|
||||||
handler = handlers[key]
|
value = data[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
@ -51,6 +51,11 @@ __all__ = (
|
|||||||
'PCMVolumeTransformer',
|
'PCMVolumeTransformer',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
CREATE_NO_WINDOW = 0
|
||||||
|
else:
|
||||||
|
CREATE_NO_WINDOW = 0x08000000
|
||||||
|
|
||||||
class AudioSource:
|
class AudioSource:
|
||||||
"""Represents an audio stream.
|
"""Represents an audio stream.
|
||||||
|
|
||||||
@ -136,7 +141,7 @@ class FFmpegAudio(AudioSource):
|
|||||||
def _spawn_process(self, args, **subprocess_kwargs):
|
def _spawn_process(self, args, **subprocess_kwargs):
|
||||||
process = None
|
process = None
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(args, **subprocess_kwargs)
|
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW, **subprocess_kwargs)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
executable = args.partition(' ')[0] if isinstance(args, str) else args[0]
|
executable = args.partition(' ')[0] if isinstance(args, str) else args[0]
|
||||||
raise ClientException(executable + ' was not found.') from None
|
raise ClientException(executable + ' was not found.') from None
|
||||||
@ -469,7 +474,7 @@ class FFmpegOpusAudio(FFmpegAudio):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _probe_codec_fallback(source, executable='ffmpeg'):
|
def _probe_codec_fallback(source, executable='ffmpeg'):
|
||||||
args = [executable, '-hide_banner', '-i', source]
|
args = [executable, '-hide_banner', '-i', source]
|
||||||
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
proc = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
out, _ = proc.communicate(timeout=20)
|
out, _ = proc.communicate(timeout=20)
|
||||||
output = out.decode('utf8')
|
output = out.decode('utf8')
|
||||||
codec = bitrate = None
|
codec = bitrate = None
|
||||||
|
@ -181,11 +181,14 @@ class ConnectionState:
|
|||||||
|
|
||||||
cache_flags._verify_intents(intents)
|
cache_flags._verify_intents(intents)
|
||||||
|
|
||||||
self._member_cache_flags = cache_flags
|
self.member_cache_flags = cache_flags
|
||||||
self._activity = activity
|
self._activity = activity
|
||||||
self._status = status
|
self._status = status
|
||||||
self._intents = intents
|
self._intents = intents
|
||||||
|
|
||||||
|
if not intents.members or cache_flags._empty:
|
||||||
|
self.store_user = self.store_user_no_intents
|
||||||
|
|
||||||
self.parsers = parsers = {}
|
self.parsers = parsers = {}
|
||||||
for attr, func in inspect.getmembers(self):
|
for attr, func in inspect.getmembers(self):
|
||||||
if attr.startswith('parse_'):
|
if attr.startswith('parse_'):
|
||||||
@ -279,6 +282,9 @@ class ConnectionState:
|
|||||||
self._users[user_id] = user
|
self._users[user_id] = user
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def store_user_no_intents(self, data):
|
||||||
|
return User(state=self, data=data)
|
||||||
|
|
||||||
def get_user(self, id):
|
def get_user(self, id):
|
||||||
return self._users.get(id)
|
return self._users.get(id)
|
||||||
|
|
||||||
@ -520,6 +526,9 @@ class ConnectionState:
|
|||||||
raw.cached_message = older_message
|
raw.cached_message = older_message
|
||||||
self.dispatch('raw_message_edit', raw)
|
self.dispatch('raw_message_edit', raw)
|
||||||
message._update(data)
|
message._update(data)
|
||||||
|
# Coerce the `after` parameter to take the new updated Member
|
||||||
|
# ref: #5999
|
||||||
|
older_message.author = message.author
|
||||||
self.dispatch('message_edit', older_message, message)
|
self.dispatch('message_edit', older_message, message)
|
||||||
else:
|
else:
|
||||||
self.dispatch('raw_message_edit', raw)
|
self.dispatch('raw_message_edit', raw)
|
||||||
@ -604,7 +613,7 @@ class ConnectionState:
|
|||||||
user = data['user']
|
user = data['user']
|
||||||
member_id = int(user['id'])
|
member_id = int(user['id'])
|
||||||
member = guild.get_member(member_id)
|
member = guild.get_member(member_id)
|
||||||
flags = self._member_cache_flags
|
flags = self.member_cache_flags
|
||||||
if member is None:
|
if member is None:
|
||||||
if 'username' not in user:
|
if 'username' not in user:
|
||||||
# sometimes we receive 'incomplete' member data post-removal.
|
# sometimes we receive 'incomplete' member data post-removal.
|
||||||
@ -742,7 +751,7 @@ class ConnectionState:
|
|||||||
return
|
return
|
||||||
|
|
||||||
member = Member(guild=guild, data=data, state=self)
|
member = Member(guild=guild, data=data, state=self)
|
||||||
if self._member_cache_flags.joined:
|
if self.member_cache_flags.joined:
|
||||||
guild._add_member(member)
|
guild._add_member(member)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -786,7 +795,7 @@ class ConnectionState:
|
|||||||
|
|
||||||
self.dispatch('member_update', old_member, member)
|
self.dispatch('member_update', old_member, member)
|
||||||
else:
|
else:
|
||||||
if self._member_cache_flags.joined:
|
if self.member_cache_flags.joined:
|
||||||
member = Member(data=data, guild=guild, state=self)
|
member = Member(data=data, guild=guild, state=self)
|
||||||
guild._add_member(member)
|
guild._add_member(member)
|
||||||
log.debug('GUILD_MEMBER_UPDATE referencing an unknown member ID: %s. Discarding.', user_id)
|
log.debug('GUILD_MEMBER_UPDATE referencing an unknown member ID: %s. Discarding.', user_id)
|
||||||
@ -817,7 +826,7 @@ class ConnectionState:
|
|||||||
return self._add_guild_from_data(data)
|
return self._add_guild_from_data(data)
|
||||||
|
|
||||||
async def chunk_guild(self, guild, *, wait=True, cache=None):
|
async def chunk_guild(self, guild, *, wait=True, cache=None):
|
||||||
cache = cache or self._member_cache_flags.joined
|
cache = cache or self.member_cache_flags.joined
|
||||||
request = self._chunk_requests.get(guild.id)
|
request = self._chunk_requests.get(guild.id)
|
||||||
if request is None:
|
if request is None:
|
||||||
self._chunk_requests[guild.id] = request = ChunkRequest(guild.id, self.loop, self._get_guild, cache=cache)
|
self._chunk_requests[guild.id] = request = ChunkRequest(guild.id, self.loop, self._get_guild, cache=cache)
|
||||||
@ -984,7 +993,7 @@ class ConnectionState:
|
|||||||
def parse_voice_state_update(self, data):
|
def parse_voice_state_update(self, data):
|
||||||
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_id'))
|
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_id'))
|
||||||
channel_id = utils._get_as_snowflake(data, 'channel_id')
|
channel_id = utils._get_as_snowflake(data, 'channel_id')
|
||||||
flags = self._member_cache_flags
|
flags = self.member_cache_flags
|
||||||
self_id = self.user.id
|
self_id = self.user.id
|
||||||
if guild is not None:
|
if guild is not None:
|
||||||
if int(data['user_id']) == self_id:
|
if int(data['user_id']) == self_id:
|
||||||
|
139
discord/sticker.py
Normal file
139
discord/sticker.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2020 Rapptz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .mixins import Hashable
|
||||||
|
from .asset import Asset
|
||||||
|
from .utils import snowflake_time
|
||||||
|
from .enums import StickerType, try_enum
|
||||||
|
|
||||||
|
class Sticker(Hashable):
|
||||||
|
"""Represents a sticker
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the name of the sticker
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if the sticker is equal to another sticker
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if the sticker is not equal to another sticker
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
The sticker's name
|
||||||
|
id: :class:`int`
|
||||||
|
The id of the sticker
|
||||||
|
description: :class:`str`
|
||||||
|
The description of the sticker
|
||||||
|
pack_id: :class:`int`
|
||||||
|
The id of the sticker's pack
|
||||||
|
format: :class:`StickerType`
|
||||||
|
The format for the sticker's image
|
||||||
|
image: :class:`str`
|
||||||
|
The sticker's image
|
||||||
|
tags: List[:class:`str`]
|
||||||
|
A list of tags for the sticker
|
||||||
|
preview_image: Optional[:class:`str`]
|
||||||
|
The sticker's preview asset hash
|
||||||
|
"""
|
||||||
|
__slots__ = ('_state', 'id', 'name', 'description', 'pack_id', 'format', 'image', 'tags', 'preview_image')
|
||||||
|
|
||||||
|
def __init__(self, *, state, data):
|
||||||
|
self._state = state
|
||||||
|
self.id = int(data['id'])
|
||||||
|
self.name = data['name']
|
||||||
|
self.description = data['description']
|
||||||
|
self.pack_id = int(data['pack_id'])
|
||||||
|
self.format = try_enum(StickerType, data['format_type'])
|
||||||
|
self.image = data['asset']
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.tags = [tag.strip() for tag in data['tags'].split(',')]
|
||||||
|
except KeyError:
|
||||||
|
self.tags = []
|
||||||
|
|
||||||
|
self.preview_image = data.get('preview_asset')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<{0.__class__.__name__} id={0.id} name={0.name!r}>'.format(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def created_at(self):
|
||||||
|
""":class:`datetime.datetime`: Returns the sticker's creation time in UTC as a naive datetime."""
|
||||||
|
return snowflake_time(self.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def image_url(self):
|
||||||
|
"""Returns an :class:`Asset` for the sticker's image.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This will return ``None`` if the format is ``StickerType.lottie``
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`Asset`]
|
||||||
|
The resulting CDN asset.
|
||||||
|
"""
|
||||||
|
return self.image_url_as()
|
||||||
|
|
||||||
|
def image_url_as(self, *, size=1024):
|
||||||
|
"""Optionally returns an :class:`Asset` for the sticker's image.
|
||||||
|
|
||||||
|
The size must be a power of 2 between 16 and 4096.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This will return ``None`` if the format is ``StickerType.lottie``.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
size: :class:`int`
|
||||||
|
The size of the image to display.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
InvalidArgument
|
||||||
|
Invalid ``size``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`Asset`]
|
||||||
|
The resulting CDN asset or ``None``.
|
||||||
|
"""
|
||||||
|
if self.format is StickerType.lottie:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Asset._from_sticker_url(self._state, self, size=size)
|
@ -68,8 +68,39 @@ class Team:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def icon_url(self):
|
def icon_url(self):
|
||||||
""":class:`.Asset`: Retrieves the team's icon asset."""
|
""":class:`.Asset`: Retrieves the team's icon asset.
|
||||||
return Asset._from_icon(self._state, self, 'team')
|
|
||||||
|
This is equivalent to calling :meth:`icon_url_as` with
|
||||||
|
the default parameters ('webp' format and a size of 1024).
|
||||||
|
"""
|
||||||
|
return self.icon_url_as()
|
||||||
|
|
||||||
|
def icon_url_as(self, *, format='None', size=1024):
|
||||||
|
"""Returns an :class:`Asset` for the icon the team has.
|
||||||
|
|
||||||
|
The format must be one of 'webp', 'jpeg', 'jpg' or 'png'.
|
||||||
|
The size must be a power of 2 between 16 and 4096.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
format: :class:`str`
|
||||||
|
The format to attempt to convert the icon to. Defaults to 'webp'.
|
||||||
|
size: :class:`int`
|
||||||
|
The size of the image to display.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
InvalidArgument
|
||||||
|
Bad image format passed to ``format`` or invalid ``size``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`Asset`
|
||||||
|
The resulting CDN asset.
|
||||||
|
"""
|
||||||
|
return Asset._from_icon(self._state, self, 'team', format=format, size=size)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def owner(self):
|
def owner(self):
|
||||||
|
@ -59,6 +59,10 @@ class _PartialTemplateState:
|
|||||||
def self_id(self):
|
def self_id(self):
|
||||||
return self.__state.user.id
|
return self.__state.user.id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def member_cache_flags(self):
|
||||||
|
return self.__state.member_cache_flags
|
||||||
|
|
||||||
def store_emoji(self, guild, packet):
|
def store_emoji(self, guild, packet):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -220,6 +220,7 @@ class VoiceClient(VoiceProtocol):
|
|||||||
self._player = None
|
self._player = None
|
||||||
self.encoder = None
|
self.encoder = None
|
||||||
self._lite_nonce = 0
|
self._lite_nonce = 0
|
||||||
|
self.ws = None
|
||||||
|
|
||||||
warn_nacl = not has_nacl
|
warn_nacl = not has_nacl
|
||||||
supported_modes = (
|
supported_modes = (
|
||||||
@ -364,6 +365,8 @@ class VoiceClient(VoiceProtocol):
|
|||||||
self._runner = self.loop.create_task(self.poll_voice_ws(reconnect))
|
self._runner = self.loop.create_task(self.poll_voice_ws(reconnect))
|
||||||
|
|
||||||
async def potential_reconnect(self):
|
async def potential_reconnect(self):
|
||||||
|
# Attempt to stop the player thread from playing early
|
||||||
|
self._connected.clear()
|
||||||
self.prepare_handshake()
|
self.prepare_handshake()
|
||||||
self._potentially_reconnecting = True
|
self._potentially_reconnecting = True
|
||||||
try:
|
try:
|
||||||
|
24
docs/api.rst
24
docs/api.rst
@ -2053,6 +2053,23 @@ of :class:`enum.Enum`.
|
|||||||
Represents the default avatar with the color red.
|
Represents the default avatar with the color red.
|
||||||
See also :attr:`Colour.red`
|
See also :attr:`Colour.red`
|
||||||
|
|
||||||
|
.. class:: StickerType
|
||||||
|
|
||||||
|
Represents the type of sticker images.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
|
.. attribute:: png
|
||||||
|
|
||||||
|
Represents a sticker with a png image.
|
||||||
|
|
||||||
|
.. attribute:: apng
|
||||||
|
|
||||||
|
Represents a sticker with an apng image.
|
||||||
|
|
||||||
|
.. attribute:: lottie
|
||||||
|
|
||||||
|
Represents a sticker with a lottie image.
|
||||||
|
|
||||||
Async Iterator
|
Async Iterator
|
||||||
----------------
|
----------------
|
||||||
@ -2976,6 +2993,12 @@ Widget
|
|||||||
.. autoclass:: Widget()
|
.. autoclass:: Widget()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Sticker
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: Sticker()
|
||||||
|
:members:
|
||||||
|
|
||||||
MessageReference
|
MessageReference
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
.. autoclass:: MessageReference()
|
.. autoclass:: MessageReference()
|
||||||
@ -3177,7 +3200,6 @@ PublicUserFlags
|
|||||||
.. autoclass:: PublicUserFlags()
|
.. autoclass:: PublicUserFlags()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ The intents that are necessary for your bot can only be dictated by yourself. Ea
|
|||||||
For example, if you want a bot that functions without spammy events like presences or typing then we could do the following:
|
For example, if you want a bot that functions without spammy events like presences or typing then we could do the following:
|
||||||
|
|
||||||
.. code-block:: python3
|
.. code-block:: python3
|
||||||
|
:emphasize-lines: 7,9,10
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
intents = discord.Intents.default()
|
intents = discord.Intents.default()
|
||||||
@ -36,6 +37,7 @@ Note that this doesn't enable :attr:`Intents.members` since it's a privileged in
|
|||||||
Another example showing a bot that only deals with messages and guild information:
|
Another example showing a bot that only deals with messages and guild information:
|
||||||
|
|
||||||
.. code-block:: python3
|
.. code-block:: python3
|
||||||
|
:emphasize-lines: 7,9,10
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
intents = discord.Intents(messages=True, guilds=True)
|
intents = discord.Intents(messages=True, guilds=True)
|
||||||
@ -155,6 +157,7 @@ Due to an :ref:`API change <intents_member_cache>` Discord is now forcing develo
|
|||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code-block:: python3
|
.. code-block:: python3
|
||||||
|
:emphasize-lines: 3,6,8,9
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
intents = discord.Intents.default()
|
intents = discord.Intents.default()
|
||||||
|
@ -1 +1 @@
|
|||||||
aiohttp>=3.6.0,<3.7.0
|
aiohttp>=3.6.0,<3.8.0
|
||||||
|
2
setup.py
2
setup.py
@ -34,7 +34,7 @@ with open('README.rst') as f:
|
|||||||
readme = f.read()
|
readme = f.read()
|
||||||
|
|
||||||
extras_require = {
|
extras_require = {
|
||||||
'voice': ['PyNaCl==1.3.0'],
|
'voice': ['PyNaCl>=1.3.0,<1.5'],
|
||||||
'docs': [
|
'docs': [
|
||||||
'sphinx==3.0.3',
|
'sphinx==3.0.3',
|
||||||
'sphinxcontrib_trio==1.1.2',
|
'sphinxcontrib_trio==1.1.2',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user