fix merge conflict

This commit is contained in:
iDutchy 2020-11-24 17:19:31 -06:00
commit 6e024871ec
22 changed files with 448 additions and 64 deletions

View File

@ -15,7 +15,7 @@ __title__ = 'discord'
__author__ = 'Rapptz'
__license__ = 'MIT'
__copyright__ = 'Copyright 2015-2020 Rapptz'
__version__ = '1.5.1.6'
__version__ = '1.6.0.6a'
__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 .raw_models import *
from .team import *
from .sticker import Sticker
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:
from logging import NullHandler

View File

@ -713,7 +713,7 @@ class GuildChannel:
async def create_invite(self, *, reason=None, **fields):
"""|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
do this.
@ -741,6 +741,9 @@ class GuildChannel:
~discord.HTTPException
Invite creation failed.
~discord.NotFound
The channel that was passed is a category or an invalid channel.
Returns
--------
:class:`~discord.Invite`

View File

@ -131,17 +131,79 @@ class AppInfo:
def icon_url(self):
""":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
"""
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
def cover_image_url(self):
""":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
"""
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
def guild(self):

View File

@ -89,19 +89,29 @@ class Asset:
return cls(state, '/avatars/{0.id}/{0.avatar}.{1}?size={2}'.format(user, format, size))
@classmethod
def _from_icon(cls, state, object, path):
def _from_icon(cls, state, object, path, *, format='webp', size=1024):
if object.icon is None:
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)
@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:
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)
@classmethod
@ -136,6 +146,12 @@ class Asset:
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):
return self.BASE + self._url if self._url is not None else ''

View File

@ -1161,8 +1161,39 @@ class GroupChannel(discord.abc.Messageable, Hashable):
@property
def icon_url(self):
""":class:`Asset`: Returns the channel's icon asset if available."""
return Asset._from_icon(self._state, self, 'channel')
""":class:`Asset`: Returns the channel's icon asset if available.
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
def created_at(self):

View File

@ -129,6 +129,19 @@ class Colour:
"""A factory method that returns a :class:`Colour` with a value of ``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
def teal(cls):
"""A factory method that returns a :class:`Colour` with a value of ``0x1abc9c``."""
@ -251,7 +264,7 @@ class Colour:
def dark_theme(cls):
"""A factory method that returns a :class:`Colour` with a value of ``0x36393F``.
This will appear transparent on Discord's dark theme.
.. versionadded:: 1.5
"""
return cls(0x36393F)

View File

@ -52,6 +52,7 @@ __all__ = (
'WebhookType',
'ExpireBehaviour',
'ExpireBehavior',
'StickerType',
)
def _create_value_cls(name):
@ -455,3 +456,8 @@ def try_enum(cls, val):
return cls._enum_value_map_[val]
except (KeyError, TypeError, AttributeError):
return val
class StickerType(Enum):
png = 1
apng = 2
lottie = 3

View File

@ -129,7 +129,7 @@ class MemberConverter(IDConverter):
"""
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] == '#':
username, _, discriminator = argument.rpartition('#')
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):
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 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
@ -206,6 +206,10 @@ class UserConverter(IDConverter):
.. versionchanged:: 1.5
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):
match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument)
@ -214,26 +218,33 @@ class UserConverter(IDConverter):
if match is not None:
user_id = int(match.group(1))
result = await ctx.bot.try_user(user_id) or _utils_get(ctx.message.mentions, id=user_id)
else:
arg = argument
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.try_user(user_id)
except discord.HTTPException:
raise UserNotFound(argument) from None
# Remove the '@' character if this is the first character from the argument
if arg[0] == '@':
# Remove first character
arg = arg[1:]
return result
# check for discriminator if it exists,
if len(arg) > 5 and arg[-5] == '#':
discrim = arg[-4:]
name = arg[:-5]
predicate = lambda u: u.name == name and u.discriminator == discrim
result = discord.utils.find(predicate, state._users.values())
if result is not None:
return result
arg = argument
predicate = lambda u: u.name == arg
# Remove the '@' character if this is the first character from the argument
if arg[0] == '@':
# Remove first character
arg = arg[1:]
# check for discriminator if it exists,
if len(arg) > 5 and arg[-5] == '#':
discrim = arg[-4:]
name = arg[:-5]
predicate = lambda u: u.name == name and u.discriminator == discrim
result = discord.utils.find(predicate, state._users.values())
if result is not None:
return result
predicate = lambda u: u.name == arg
result = discord.utils.find(predicate, state._users.values())
if result is None:
raise UserNotFound(argument)
@ -254,12 +265,11 @@ class MessageConverter(Converter):
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound`, `MessageNotFound` or `ChannelNotReadable` instead of generic :exc:`.BadArgument`
"""
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(
r'^https?://(?:(ptb|canary)\.)?discord(?:app)?\.com/channels/'
r'(?:([0-9]{15,21})|(@me))'
r'https?://(?:(ptb|canary|www)\.)?discord(?:app)?\.com/channels/'
r'(?:[0-9]{15,21}|@me)'
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)

View File

@ -868,6 +868,10 @@ class MemberCacheFlags(BaseFlags):
self.value = self.DEFAULT_VALUE
return self
@property
def _empty(self):
return self.value == self.DEFAULT_VALUE
@flag_value
def online(self):
""":class:`bool`: Whether to cache members with a status.

View File

@ -308,8 +308,8 @@ class Guild(Hashable):
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')
cache_online_members = self._state._member_cache_flags.online
cache_joined = self._state._member_cache_flags.joined
cache_online_members = self._state.member_cache_flags.online
cache_joined = self._state.member_cache_flags.joined
self_id = self._state.self_id
for mdata in guild.get('members', []):
member = Member(data=mdata, guild=self, state=state)
@ -1253,7 +1253,8 @@ class Guild(Hashable):
def fetch_members(self, *, limit=1000, after=None):
"""|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::
@ -1274,6 +1275,8 @@ class Guild(Hashable):
Raises
------
ClientException
The members intent is not enabled.
HTTPException
Getting the members failed.
@ -1295,6 +1298,10 @@ class Guild(Hashable):
members = await guild.fetch_members(limit=150).flatten()
# 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)
async def try_member(self, member_id):

View File

@ -241,6 +241,8 @@ class HTTPClient:
raise Forbidden(r, data)
elif r.status == 404:
raise NotFound(r, data)
elif r.status == 503:
raise DiscordServerError(r, data)
else:
raise HTTPException(r, data)

View File

@ -43,6 +43,7 @@ from .file import File
from .utils import escape_mentions
from .guild import Guild
from .mixins import Hashable
from .sticker import Sticker
class Attachment:
@ -286,11 +287,15 @@ class MessageReference:
def flatten_handlers(cls):
prefix = len('_handle_')
cls._HANDLERS = {
key[prefix:]: value
handlers = [
(key[prefix:], value)
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 = [
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.
- ``icon``: A string representing the icon ID of the application.
- ``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',
@ -399,8 +408,8 @@ class Message(Hashable):
'_cs_channel_mentions', '_cs_raw_mentions', 'attachments',
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
'role_mentions', '_cs_raw_role_mentions', 'type', 'call', 'flags',
'_cs_system_content', '_cs_guild', '_state', 'reactions', 'reference',
'application', 'activity')
'_cs_system_content', '_cs_guild', '_state', 'reactions', 'reference',
'application', 'activity', 'stickers')
def __init__(self, *, state, channel, data):
self._state = state
@ -420,6 +429,7 @@ class Message(Hashable):
self.tts = data['tts']
self.content = data['content']
self.nonce = data.get('nonce')
self.stickers = [Sticker(data=data, state=state) for data in data.get('stickers', [])]
ref = data.get('message_reference')
self.reference = MessageReference(state, **ref) if ref is not None else None
@ -496,10 +506,13 @@ class Message(Hashable):
return reaction
def _update(self, data):
handlers = self._HANDLERS
for key, value in data.items():
# In an update scheme, 'author' key has to be handled before 'member'
# 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:
handler = handlers[key]
value = data[key]
except KeyError:
continue
else:

View File

@ -51,6 +51,11 @@ __all__ = (
'PCMVolumeTransformer',
)
if sys.platform != 'win32':
CREATE_NO_WINDOW = 0
else:
CREATE_NO_WINDOW = 0x08000000
class AudioSource:
"""Represents an audio stream.
@ -136,7 +141,7 @@ class FFmpegAudio(AudioSource):
def _spawn_process(self, args, **subprocess_kwargs):
process = None
try:
process = subprocess.Popen(args, **subprocess_kwargs)
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW, **subprocess_kwargs)
except FileNotFoundError:
executable = args.partition(' ')[0] if isinstance(args, str) else args[0]
raise ClientException(executable + ' was not found.') from None
@ -469,7 +474,7 @@ class FFmpegOpusAudio(FFmpegAudio):
@staticmethod
def _probe_codec_fallback(source, executable='ffmpeg'):
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)
output = out.decode('utf8')
codec = bitrate = None

View File

@ -181,11 +181,14 @@ class ConnectionState:
cache_flags._verify_intents(intents)
self._member_cache_flags = cache_flags
self.member_cache_flags = cache_flags
self._activity = activity
self._status = status
self._intents = intents
if not intents.members or cache_flags._empty:
self.store_user = self.store_user_no_intents
self.parsers = parsers = {}
for attr, func in inspect.getmembers(self):
if attr.startswith('parse_'):
@ -279,6 +282,9 @@ class ConnectionState:
self._users[user_id] = user
return user
def store_user_no_intents(self, data):
return User(state=self, data=data)
def get_user(self, id):
return self._users.get(id)
@ -520,6 +526,9 @@ class ConnectionState:
raw.cached_message = older_message
self.dispatch('raw_message_edit', raw)
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)
else:
self.dispatch('raw_message_edit', raw)
@ -604,7 +613,7 @@ class ConnectionState:
user = data['user']
member_id = int(user['id'])
member = guild.get_member(member_id)
flags = self._member_cache_flags
flags = self.member_cache_flags
if member is None:
if 'username' not in user:
# sometimes we receive 'incomplete' member data post-removal.
@ -742,7 +751,7 @@ class ConnectionState:
return
member = Member(guild=guild, data=data, state=self)
if self._member_cache_flags.joined:
if self.member_cache_flags.joined:
guild._add_member(member)
try:
@ -786,7 +795,7 @@ class ConnectionState:
self.dispatch('member_update', old_member, member)
else:
if self._member_cache_flags.joined:
if self.member_cache_flags.joined:
member = Member(data=data, guild=guild, state=self)
guild._add_member(member)
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)
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)
if request is None:
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):
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_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
if guild is not None:
if int(data['user_id']) == self_id:

139
discord/sticker.py Normal file
View 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)

View File

@ -68,8 +68,39 @@ class Team:
@property
def icon_url(self):
""":class:`.Asset`: Retrieves the team's icon asset."""
return Asset._from_icon(self._state, self, 'team')
""":class:`.Asset`: Retrieves the team's icon asset.
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
def owner(self):

View File

@ -42,11 +42,11 @@ class _PartialTemplateState:
def __init__(self, *, state):
self.__state = state
self.http = _FriendlyHttpAttributeErrorHelper()
@property
def is_bot(self):
return self.__state.is_bot
@property
def shard_count(self):
return self.__state.shard_count
@ -54,14 +54,18 @@ class _PartialTemplateState:
@property
def user(self):
return self.__state.user
@property
def self_id(self):
return self.__state.user.id
@property
def member_cache_flags(self):
return self.__state.member_cache_flags
def store_emoji(self, guild, packet):
return None
def _get_voice_client(self, id):
return None

View File

@ -220,6 +220,7 @@ class VoiceClient(VoiceProtocol):
self._player = None
self.encoder = None
self._lite_nonce = 0
self.ws = None
warn_nacl = not has_nacl
supported_modes = (
@ -364,6 +365,8 @@ class VoiceClient(VoiceProtocol):
self._runner = self.loop.create_task(self.poll_voice_ws(reconnect))
async def potential_reconnect(self):
# Attempt to stop the player thread from playing early
self._connected.clear()
self.prepare_handshake()
self._potentially_reconnecting = True
try:

View File

@ -2053,6 +2053,23 @@ of :class:`enum.Enum`.
Represents the default avatar with the color 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
----------------
@ -2976,6 +2993,12 @@ Widget
.. autoclass:: Widget()
:members:
Sticker
~~~~~~~~~~~~~~~
.. autoclass:: Sticker()
:members:
MessageReference
~~~~~~~~~~~~~~~~~
.. autoclass:: MessageReference()
@ -3177,7 +3200,6 @@ PublicUserFlags
.. autoclass:: PublicUserFlags()
:members:
Exceptions
------------

View File

@ -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:
.. code-block:: python3
:emphasize-lines: 7,9,10
import discord
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:
.. code-block:: python3
:emphasize-lines: 7,9,10
import discord
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:
.. code-block:: python3
:emphasize-lines: 3,6,8,9
import discord
intents = discord.Intents.default()

View File

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

View File

@ -34,7 +34,7 @@ with open('README.rst') as f:
readme = f.read()
extras_require = {
'voice': ['PyNaCl==1.3.0'],
'voice': ['PyNaCl>=1.3.0,<1.5'],
'docs': [
'sphinx==3.0.3',
'sphinxcontrib_trio==1.1.2',