aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNCPlayz <[email protected]>2019-11-20 19:57:34 +0000
committerRapptz <[email protected]>2019-12-20 20:56:40 -0500
commit9c6a121644a33ab27f00c5241a90fdfcc07f3ffa (patch)
tree369f32fc26e0e1840b664adc83eed23860a5509e
parentDefault the Streaming name to the one passed for user created ones. (diff)
downloaddiscord.py-9c6a121644a33ab27f00c5241a90fdfcc07f3ffa.tar.xz
discord.py-9c6a121644a33ab27f00c5241a90fdfcc07f3ffa.zip
Implement discord.MessageFlags
Refactor flags placement and use it for suppression.
-rw-r--r--discord/__init__.py3
-rw-r--r--discord/flags.py237
-rw-r--r--discord/guild.py115
-rw-r--r--discord/http.py10
-rw-r--r--discord/message.py38
-rw-r--r--docs/api.rst6
6 files changed, 284 insertions, 125 deletions
diff --git a/discord/__init__.py b/discord/__init__.py
index eade921e..51b257f6 100644
--- a/discord/__init__.py
+++ b/discord/__init__.py
@@ -27,7 +27,8 @@ from .emoji import Emoji
from .partial_emoji import PartialEmoji
from .activity import *
from .channel import *
-from .guild import Guild, SystemChannelFlags
+from .guild import Guild
+from .flags import SystemChannelFlags, MessageFlags
from .relationship import Relationship
from .member import Member, VoiceState
from .message import Message, Attachment
diff --git a/discord/flags.py b/discord/flags.py
new file mode 100644
index 00000000..1c3ce536
--- /dev/null
+++ b/discord/flags.py
@@ -0,0 +1,237 @@
+# -*- coding: utf-8 -*-
+
+"""
+The MIT License (MIT)
+
+Copyright (c) 2015-2019 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.
+"""
+
+__all__ = (
+ 'SystemChannelFlags',
+ 'MessageFlags',
+)
+
+class _flag_descriptor:
+ def __init__(self, func):
+ self.flag = func(None)
+ self.__doc__ = func.__doc__
+
+ def __get__(self, instance, owner):
+ return instance._has_flag(self.flag)
+
+ def __set__(self, instance, value):
+ instance._set_flag(self.flag, value)
+
+def fill_with_flags(cls):
+ cls.VALID_FLAGS = {
+ name: value.flag
+ for name, value in cls.__dict__.items()
+ if isinstance(value, _flag_descriptor)
+ }
+
+ max_bits = max(cls.VALID_FLAGS.values()).bit_length()
+ cls.ALL_OFF_VALUE = -1 + (2 ** max_bits)
+ return cls
+
+@fill_with_flags
+class SystemChannelFlags:
+ r"""Wraps up a Discord system channel flag value.
+
+ Similar to :class:`Permissions`\, the properties provided are two way.
+ You can set and retrieve individual bits using the properties as if they
+ were regular bools. This allows you to edit the system flags easily.
+
+ To construct an object you can pass keyword arguments denoting the flags
+ to enable or disable.
+
+ .. container:: operations
+
+ .. describe:: x == y
+
+ Checks if two flags are equal.
+ .. describe:: x != y
+
+ Checks if two flags are not equal.
+ .. describe:: hash(x)
+
+ Return the flag's hash.
+ .. describe:: iter(x)
+
+ Returns an iterator of ``(name, value)`` pairs. This allows it
+ to be, for example, constructed as a dict or a list of pairs.
+
+ Attributes
+ -----------
+ value: :class:`int`
+ The raw value. This value is a bit array field of a 53-bit integer
+ representing the currently available flags. You should query
+ flags via the properties rather than using this raw value.
+ """
+ __slots__ = ('value',)
+
+ def __init__(self, **kwargs):
+ self.value = self.ALL_OFF_VALUE
+ for key, value in kwargs.items():
+ if key not in self.VALID_FLAGS:
+ raise TypeError('%r is not a valid flag name.' % key)
+ setattr(self, key, value)
+
+ @classmethod
+ def _from_value(cls, value):
+ self = cls.__new__(cls)
+ self.value = value
+ return self
+
+ def __eq__(self, other):
+ return isinstance(other, SystemChannelFlags) and self.value == other.value
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self.value)
+
+ def __repr__(self):
+ return '<SystemChannelFlags value=%s>' % self.value
+
+ def __iter__(self):
+ for name, value in self.__class__.__dict__.items():
+ if isinstance(value, _flag_descriptor):
+ yield (name, self._has_flag(value.flag))
+
+ # For some reason the flags for system channels are "inverted"
+ # ergo, if they're set then it means "suppress" (off in the GUI toggle)
+ # Since this is counter-intuitive from an API perspective and annoying
+ # these will be inverted automatically
+
+ def _has_flag(self, o):
+ return (self.value & o) != o
+
+ def _set_flag(self, o, toggle):
+ if toggle is True:
+ self.value &= ~o
+ elif toggle is False:
+ self.value |= o
+ else:
+ raise TypeError('Value to set for SystemChannelFlags must be a bool.')
+
+ @_flag_descriptor
+ def join_notifications(self):
+ """:class:`bool`: Returns ``True`` if the system channel is used for member join notifications."""
+ return 1
+
+ @_flag_descriptor
+ def premium_subscriptions(self):
+ """:class:`bool`: Returns ``True`` if the system channel is used for Nitro boosting notifications."""
+ return 2
+
+
+@fill_with_flags
+class MessageFlags:
+ r"""Wraps up a Discord Message flag value.
+
+ See :class:`SystemChannelFlags`.
+
+ .. container:: operations
+
+ .. describe:: x == y
+
+ Checks if two flags are equal.
+ .. describe:: x != y
+
+ Checks if two flags are not equal.
+ .. describe:: hash(x)
+
+ Return the flag's hash.
+ .. describe:: iter(x)
+
+ Returns an iterator of ``(name, value)`` pairs. This allows it
+ to be, for example, constructed as a dict or a list of pairs.
+
+ Attributes
+ -----------
+ value: :class:`int`
+ The raw value. This value is a bit array field of a 53-bit integer
+ representing the currently available flags. You should query
+ flags via the properties rather than using this raw value.
+ """
+ __slots__ = ('value',)
+
+ def __init__(self, **kwargs):
+ self.value = 0
+ for key, value in kwargs.items():
+ if key not in self.VALID_FLAGS:
+ raise TypeError('%r is not a valid flag name.' % key)
+ setattr(self, key, value)
+
+ @classmethod
+ def _from_value(cls, value):
+ self = cls.__new__(cls)
+ self.value = value
+ return self
+
+ def __eq__(self, other):
+ return isinstance(other, MessageFlags) and self.value == other.value
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self.value)
+
+ def __repr__(self):
+ return '<MessageFlags value=%s>' % self.value
+
+ def __iter__(self):
+ for name, value in self.__class__.__dict__.items():
+ if isinstance(value, _flag_descriptor):
+ yield (name, self._has_flag(value.flag))
+
+ def _has_flag(self, o):
+ return (self.value & o) == o
+
+ def _set_flag(self, o, toggle):
+ if toggle is True:
+ self.value |= o
+ elif toggle is False:
+ self.value &= o
+ else:
+ raise TypeError('Value to set for MessageFlags must be a bool.')
+
+ @_flag_descriptor
+ def crossposted(self):
+ """:class:`bool`: Returns ``True`` if the message is the original crossposted message."""
+ return 1
+
+ @_flag_descriptor
+ def is_crossposted(self):
+ """:class:`bool`: Returns ``True`` if the message was crossposted from another channel."""
+ return 2
+
+ @_flag_descriptor
+ def suppress_embeds(self):
+ """:class:`bool`: Returns ``True`` if the message's embeds have been suppressed."""
+ return 4
+
+ @_flag_descriptor
+ def source_message_deleted(self):
+ """:class:`bool`: Returns ``True`` if the source message for this crosspost has been deleted."""
+ return 8
diff --git a/discord/guild.py b/discord/guild.py
index e4402646..b617e8ac 100644
--- a/discord/guild.py
+++ b/discord/guild.py
@@ -45,124 +45,11 @@ from .iterators import AuditLogIterator, MemberIterator
from .webhook import Webhook
from .widget import Widget
from .asset import Asset
+from .flags import SystemChannelFlags
BanEntry = namedtuple('BanEntry', 'reason user')
_GuildLimit = namedtuple('_GuildLimit', 'emoji bitrate filesize')
-class _flag_descriptor:
- def __init__(self, func):
- self.flag = func(None)
- self.__doc__ = func.__doc__
-
- def __get__(self, instance, owner):
- return instance._has_flag(self.flag)
-
- def __set__(self, instance, value):
- instance._set_flag(self.flag, value)
-
-def fill_with_flags(cls):
- cls.VALID_FLAGS = {
- name: value.flag
- for name, value in cls.__dict__.items()
- if isinstance(value, _flag_descriptor)
- }
-
- max_bits = max(cls.VALID_FLAGS.values()).bit_length()
- cls.ALL_OFF_VALUE = -1 + (2 ** max_bits)
- return cls
-
-@fill_with_flags
-class SystemChannelFlags:
- r"""Wraps up a Discord system channel flag value.
-
- Similar to :class:`Permissions`\, the properties provided are two way.
- You can set and retrieve individual bits using the properties as if they
- were regular bools. This allows you to edit the system flags easily.
-
- To construct an object you can pass keyword arguments denoting the flags
- to enable or disable.
-
- .. container:: operations
-
- .. describe:: x == y
-
- Checks if two flags are equal.
- .. describe:: x != y
-
- Checks if two flags are not equal.
- .. describe:: hash(x)
-
- Return the flag's hash.
- .. describe:: iter(x)
-
- Returns an iterator of ``(name, value)`` pairs. This allows it
- to be, for example, constructed as a dict or a list of pairs.
-
- Attributes
- -----------
- value: :class:`int`
- The raw value. This value is a bit array field of a 53-bit integer
- representing the currently available flags. You should query
- flags via the properties rather than using this raw value.
- """
- __slots__ = ('value',)
-
- def __init__(self, **kwargs):
- self.value = self.ALL_OFF_VALUE
- for key, value in kwargs.items():
- if key not in self.VALID_FLAGS:
- raise TypeError('%r is not a valid flag name.' % key)
- setattr(self, key, value)
-
- @classmethod
- def _from_value(cls, value):
- self = cls.__new__(cls)
- self.value = value
- return self
-
- def __eq__(self, other):
- return isinstance(other, SystemChannelFlags) and self.value == other.value
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __hash__(self):
- return hash(self.value)
-
- def __repr__(self):
- return '<SystemChannelFlags value=%s>' % self.value
-
- def __iter__(self):
- for name, value in self.__class__.__dict__.items():
- if isinstance(value, _flag_descriptor):
- yield (name, self._has_flag(value.flag))
-
- # For some reason the flags in the Discord API are "inverted"
- # ergo, if they're set then it means "suppress" (off in the GUI toggle)
- # Since this is counter-intuitive from an API perspective and annoying
- # these will be inverted automatically
-
- def _has_flag(self, o):
- return (self.value & o) != o
-
- def _set_flag(self, o, toggle):
- if toggle is True:
- self.value &= ~o
- elif toggle is False:
- self.value |= o
- else:
- raise TypeError('Value to set for SystemChannelFlags must be a bool.')
-
- @_flag_descriptor
- def join_notifications(self):
- """:class:`bool`: Returns True if the system channel is used for member join notifications."""
- return 1
-
- @_flag_descriptor
- def premium_subscriptions(self):
- """:class:`bool`: Returns True if the system channel is used for Nitro boosting notifications."""
- return 2
-
class Guild(Hashable):
"""Represents a Discord guild.
diff --git a/discord/http.py b/discord/http.py
index 81d60a42..0c0a5497 100644
--- a/discord/http.py
+++ b/discord/http.py
@@ -372,12 +372,6 @@ class HTTPClient:
r = Route('PATCH', '/channels/{channel_id}/messages/{message_id}', channel_id=channel_id, message_id=message_id)
return self.request(r, json=fields)
- def suppress_message_embeds(self, channel_id, message_id, *, suppress):
- payload = { 'suppress': suppress }
- r = Route('POST', '/channels/{channel_id}/messages/{message_id}/suppress-embeds',
- channel_id=channel_id, message_id=message_id)
- return self.request(r, json=payload)
-
def add_reaction(self, channel_id, message_id, emoji):
r = Route('PUT', '/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me',
channel_id=channel_id, message_id=message_id, emoji=emoji)
@@ -430,6 +424,10 @@ class HTTPClient:
return self.request(Route('GET', '/channels/{channel_id}/messages', channel_id=channel_id), params=params)
+ def publish_message(self, channel_id, message_id):
+ return self.request(Route('POST', '/channels/{channel_id}/messages/{message_id}/crosspost',
+ channel_id=channel_id, message_id=message_id))
+
def pin_message(self, channel_id, message_id):
return self.request(Route('PUT', '/channels/{channel_id}/pins/{message_id}',
channel_id=channel_id, message_id=message_id))
diff --git a/discord/message.py b/discord/message.py
index 1fd2245a..01ef34db 100644
--- a/discord/message.py
+++ b/discord/message.py
@@ -38,6 +38,7 @@ from .enums import MessageType, try_enum
from .errors import InvalidArgument, ClientException, HTTPException
from .embeds import Embed
from .member import Member
+from .flags import MessageFlags
class Attachment:
"""Represents an attachment from Discord.
@@ -237,6 +238,8 @@ class Message:
A list of attachments given to a message.
pinned: :class:`bool`
Specifies if the message is currently pinned.
+ flags: :class:`MessageFlags`
+ Extra features of the message.
reactions : List[:class:`Reaction`]
Reactions to a message. Reactions can be either custom emoji or standard unicode emoji.
activity: Optional[:class:`dict`]
@@ -263,7 +266,7 @@ class Message:
'mention_everyone', 'embeds', 'id', 'mentions', 'author',
'_cs_channel_mentions', '_cs_raw_mentions', 'attachments',
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
- 'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
+ 'role_mentions', '_cs_raw_role_mentions', 'type', 'call', 'flags',
'_cs_system_content', '_cs_guild', '_state', 'reactions',
'application', 'activity')
@@ -280,19 +283,20 @@ class Message:
self._edited_timestamp = utils.parse_time(data['edited_timestamp'])
self.type = try_enum(MessageType, data['type'])
self.pinned = data['pinned']
+ self.flags = MessageFlags._from_value(data.get('flags', 0))
self.mention_everyone = data['mention_everyone']
self.tts = data['tts']
self.content = data['content']
self.nonce = data.get('nonce')
- for handler in ('author', 'member', 'mentions', 'mention_roles', 'call'):
+ for handler in ('author', 'member', 'mentions', 'mention_roles', 'call', 'flags'):
try:
getattr(self, '_handle_%s' % handler)(data[handler])
except KeyError:
continue
def __repr__(self):
- return '<Message id={0.id} channel={0.channel!r} type={0.type!r} author={0.author!r}>'.format(self)
+ return '<Message id={0.id} channel={0.channel!r} type={0.type!r} author={0.author!r} flags={0.flags!r}>'.format(self)
def _try_patch(self, data, key, transform=None):
try:
@@ -361,6 +365,9 @@ class Message:
def _handle_pinned(self, value):
self.pinned = value
+ def _handle_flags(self, value):
+ self.flags = MessageFlags._from_value(value)
+
def _handle_application(self, value):
self.application = value
@@ -772,7 +779,9 @@ class Message:
except KeyError:
pass
else:
- await self._state.http.suppress_message_embeds(self.channel.id, self.id, suppress=suppress)
+ flags = MessageFlags._from_value(self.flags.value)
+ flags.suppress_embeds = suppress
+ fields['flags'] = flags.value
delete_after = fields.pop('delete_after', None)
@@ -783,6 +792,27 @@ class Message:
if delete_after is not None:
await self.delete(delay=delete_after)
+ async def publish(self):
+ """|coro|
+
+ Publishes this message to your announcement channel.
+
+ You must have the :attr:`~Permissions.manage_messages` permission to use this.
+
+ .. note::
+
+ This can only be used by non-bot accounts.
+
+ Raises
+ -------
+ Forbidden
+ You do not have the proper permissions to publish this message.
+ HTTPException
+ Publishing the message failed.
+ """
+
+ await self._state.http.publish_message(self.channel.id, self.id)
+
async def pin(self):
"""|coro|
diff --git a/docs/api.rst b/docs/api.rst
index 46bbe2a6..f929ea26 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -2507,6 +2507,12 @@ SystemChannelFlags
.. autoclass:: SystemChannelFlags
:members:
+MessageFlags
+~~~~~~~~~~~~
+
+.. autoclass:: MessageFlags
+ :members:
+
Exceptions
------------