Add support for guild widget
This commit is contained in:
		| @@ -36,6 +36,7 @@ from .role import Role | ||||
| from .file import File | ||||
| from .colour import Color, Colour | ||||
| from .invite import Invite, PartialInviteChannel, PartialInviteGuild | ||||
| from .widget import Widget, WidgetMember, WidgetChannel | ||||
| from .object import Object | ||||
| from .reaction import Reaction | ||||
| from . import utils, opus, abc | ||||
|   | ||||
| @@ -27,7 +27,6 @@ DEALINGS IN THE SOFTWARE. | ||||
| import asyncio | ||||
| from collections import namedtuple | ||||
| import logging | ||||
| import re | ||||
| import signal | ||||
| import sys | ||||
| import traceback | ||||
| @@ -37,7 +36,7 @@ import websockets | ||||
|  | ||||
| from .user import User, Profile | ||||
| from .invite import Invite | ||||
| from .object import Object | ||||
| from .widget import Widget | ||||
| from .guild import Guild | ||||
| from .member import Member | ||||
| from .errors import * | ||||
| @@ -170,16 +169,6 @@ class Client: | ||||
|     def _handle_ready(self): | ||||
|         self._ready.set() | ||||
|  | ||||
|     def _resolve_invite(self, invite): | ||||
|         if isinstance(invite, Invite) or isinstance(invite, Object): | ||||
|             return invite.id | ||||
|         else: | ||||
|             rx = r'(?:https?\:\/\/)?discord(?:\.gg|app\.com\/invite)\/(.+)' | ||||
|             m = re.match(rx, invite) | ||||
|             if m: | ||||
|                 return m.group(1) | ||||
|         return invite | ||||
|  | ||||
|     @property | ||||
|     def latency(self): | ||||
|         """:class:`float`: Measures latency between a HEARTBEAT and a HEARTBEAT_ACK in seconds. | ||||
| @@ -991,7 +980,7 @@ class Client: | ||||
|             The invite from the URL/ID. | ||||
|         """ | ||||
|  | ||||
|         invite_id = self._resolve_invite(url) | ||||
|         invite_id = utils.resolve_invite(url) | ||||
|         data = await self.http.get_invite(invite_id, with_counts=with_counts) | ||||
|         return Invite.from_incomplete(state=self._connection, data=data) | ||||
|  | ||||
| @@ -1018,11 +1007,41 @@ class Client: | ||||
|             Revoking the invite failed. | ||||
|         """ | ||||
|  | ||||
|         invite_id = self._resolve_invite(invite) | ||||
|         invite_id = utils.resolve_invite(invite) | ||||
|         await self.http.delete_invite(invite_id) | ||||
|  | ||||
|     # Miscellaneous stuff | ||||
|  | ||||
|     async def fetch_widget(self, guild_id): | ||||
|         """|coro| | ||||
|  | ||||
|         Gets a :class:`Widget` from a guild ID. | ||||
|  | ||||
|         .. note:: | ||||
|  | ||||
|             The guild must have the widget enabled to get this information. | ||||
|  | ||||
|         Parameters | ||||
|         ----------- | ||||
|         guild_id: :class:`int` | ||||
|             The ID of the guild. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         Forbidden | ||||
|             The widget for this guild is disabled. | ||||
|         HTTPException | ||||
|             Retrieving the widget failed. | ||||
|  | ||||
|         Returns | ||||
|         -------- | ||||
|         :class:`Widget` | ||||
|             The guild's widget. | ||||
|         """ | ||||
|         data = await self.http.get_widget(guild_id) | ||||
|  | ||||
|         return Widget(state=self._connection, data=data) | ||||
|  | ||||
|     async def application_info(self): | ||||
|         """|coro| | ||||
|  | ||||
|   | ||||
| @@ -42,6 +42,7 @@ from .user import User | ||||
| from .invite import Invite | ||||
| from .iterators import AuditLogIterator | ||||
| from .webhook import Webhook | ||||
| from .widget import Widget | ||||
|  | ||||
| VALID_ICON_FORMATS = {"jpeg", "jpg", "webp", "png"} | ||||
|  | ||||
| @@ -1475,3 +1476,28 @@ class Guild(Hashable): | ||||
|  | ||||
|         return AuditLogIterator(self, before=before, after=after, limit=limit, | ||||
|                                 reverse=reverse, user_id=user, action_type=action) | ||||
|      | ||||
|     async def widget(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Returns the widget of the guild. | ||||
|  | ||||
|         .. note:: | ||||
|  | ||||
|             The guild must have the widget enabled to get this information. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         Forbidden | ||||
|             The widget for this guild is disabled. | ||||
|         HTTPException | ||||
|             Retrieving the widget failed. | ||||
|  | ||||
|         Returns | ||||
|         -------- | ||||
|         :class:`Widget` | ||||
|             The guild's widget. | ||||
|         """ | ||||
|         data = await self._state.http.get_widget(self.id) | ||||
|  | ||||
|         return Widget(state=self._state, data=data) | ||||
|   | ||||
| @@ -659,6 +659,9 @@ class HTTPClient: | ||||
|         r = Route('GET', '/guilds/{guild_id}/audit-logs', guild_id=guild_id) | ||||
|         return self.request(r, params=params) | ||||
|  | ||||
|     def get_widget(self, guild_id): | ||||
|         return self.request(Route('GET', '/guilds/{guild_id}/widget.json', guild_id=guild_id)) | ||||
|  | ||||
|     # Invite management | ||||
|  | ||||
|     def create_invite(self, channel_id, *, reason=None, **options): | ||||
|   | ||||
| @@ -38,6 +38,7 @@ import re | ||||
| import warnings | ||||
|  | ||||
| from .errors import InvalidArgument | ||||
| from .object import Object | ||||
|  | ||||
| DISCORD_EPOCH = 1420070400000 | ||||
|  | ||||
| @@ -340,3 +341,28 @@ def _string_width(string, *, _IS_ASCII=_IS_ASCII): | ||||
|     for char in string: | ||||
|         width += 2 if func(char) in UNICODE_WIDE_CHAR_TYPE else 1 | ||||
|     return width | ||||
|  | ||||
| def resolve_invite(invite): | ||||
|     """ | ||||
|     Resolves an invite from a :class:`Invite`, URL or ID | ||||
|  | ||||
|     Parameters | ||||
|     ----------- | ||||
|     invite: Union[:class:`Invite`, :class:`Object`, :class:`str`] | ||||
|         The invite. | ||||
|  | ||||
|     Returns | ||||
|     -------- | ||||
|     :class:`str` | ||||
|         The invite code. | ||||
|     """ | ||||
|     from .invite import Invite  # circular import | ||||
|     if isinstance(invite, Invite) or isinstance(invite, Object): | ||||
|         return invite.id | ||||
|     else: | ||||
|         rx = r'(?:https?\:\/\/)?discord(?:\.gg|app\.com\/invite)\/(.+)' | ||||
|         m = re.match(rx, invite) | ||||
|         if m: | ||||
|             return m.group(1) | ||||
|     return invite | ||||
|  | ||||
|   | ||||
							
								
								
									
										244
									
								
								discord/widget.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								discord/widget.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| # -*- 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. | ||||
| """ | ||||
|  | ||||
| from .utils import snowflake_time, _get_as_snowflake, resolve_invite | ||||
| from .user import BaseUser | ||||
| from .activity import Activity | ||||
| from .invite import Invite | ||||
| from .enums import Status, try_enum | ||||
| from collections import namedtuple | ||||
|  | ||||
| VALID_ICON_FORMATS = {"jpeg", "jpg", "webp", "png"} | ||||
|  | ||||
| class WidgetChannel(namedtuple('WidgetChannel', 'id name position')): | ||||
|     """Represents a "partial" widget channel. | ||||
|  | ||||
|     .. container:: operations | ||||
|  | ||||
|         .. describe:: x == y | ||||
|  | ||||
|             Checks if two partial channels are the same. | ||||
|  | ||||
|         .. describe:: x != y | ||||
|  | ||||
|             Checks if two partial channels are not the same. | ||||
|  | ||||
|         .. describe:: hash(x) | ||||
|  | ||||
|             Return the partial channel's hash. | ||||
|  | ||||
|         .. describe:: str(x) | ||||
|  | ||||
|             Returns the partial channel's name. | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     id: :class:`int` | ||||
|         The channel's ID. | ||||
|     name: :class:`str` | ||||
|         The channel's name. | ||||
|     position: :class:`int` | ||||
|         The channel's position | ||||
|     """ | ||||
|     __slots__ = () | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
|  | ||||
|     @property | ||||
|     def mention(self): | ||||
|         """:class:`str`: The string that allows you to mention the channel.""" | ||||
|         return '<#%s>' % self.id | ||||
|  | ||||
|     @property | ||||
|     def created_at(self): | ||||
|         """Returns the channel's creation time in UTC.""" | ||||
|         return snowflake_time(self.id) | ||||
|  | ||||
| class WidgetMember(BaseUser): | ||||
|     """Represents a "partial" member of the widget's guild. | ||||
|  | ||||
|     .. container:: operations | ||||
|  | ||||
|         .. describe:: x == y | ||||
|  | ||||
|             Checks if two widget members are the same. | ||||
|  | ||||
|         .. describe:: x != y | ||||
|  | ||||
|             Checks if two widget members are not the same. | ||||
|  | ||||
|         .. describe:: hash(x) | ||||
|  | ||||
|             Return the widget member's hash. | ||||
|  | ||||
|         .. describe:: str(x) | ||||
|  | ||||
|             Returns the widget member's `name#discriminator`. | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     id: :class:`int` | ||||
|         The member's ID. | ||||
|     name: :class:`str` | ||||
|         The member's username. | ||||
|     discriminator: :class:`str` | ||||
|         The member's discriminator. | ||||
|     bot: :class:`bool` | ||||
|         Whether the member is a bot. | ||||
|     status: :class:`Status` | ||||
|         The member's status. | ||||
|     nick: Optional[:class:`str`] | ||||
|         The member's nickname. | ||||
|     avatar: Optional[:class:`str`] | ||||
|         The member's avatar hash. | ||||
|     activity: Optional[:class:`Activity`] | ||||
|         The member's activity. | ||||
|     deafened: Optional[:class:`bool`] | ||||
|         Whether the member is currently deafened. | ||||
|     muted: Optional[:class:`bool`] | ||||
|         Whether the member is currently muted. | ||||
|     suppress: Optional[:class:`bool`] | ||||
|         Whether the member is currently being suppressed. | ||||
|     connected_channel: Optional[:class:`VoiceChannel`] | ||||
|         Which channel the member is connected to. | ||||
|     """ | ||||
|     __slots__ = ('name', 'status', 'nick', 'avatar', 'discriminator', | ||||
|                  'id', 'bot', 'activity', 'deafened', 'suppress', 'muted', | ||||
|                  'connected_channel') | ||||
|  | ||||
|     def __init__(self, *, state, data, connected_channel=None): | ||||
|         super().__init__(state=state, data=data) | ||||
|         self.nick = data.get('nick') | ||||
|         self.status = try_enum(Status, data.get('status')) | ||||
|         self.deafened = data.get('deaf', False) or data.get('self_deaf', False) | ||||
|         self.muted = data.get('mute', False) or data.get('self_mute', False) | ||||
|         self.suppress = data.get('suppress', False) | ||||
|  | ||||
|         game = data.get('game') | ||||
|         if game: | ||||
|             self.activity = Activity(**game) | ||||
|  | ||||
|         self.connected_channel = connected_channel | ||||
|  | ||||
|     @property | ||||
|     def display_name(self): | ||||
|         """:class:`str`: Returns the member's display name.""" | ||||
|         return self.nick if self.nick else self.name | ||||
|  | ||||
| class Widget: | ||||
|     """Represents a :class:`Guild` widget. | ||||
|  | ||||
|     .. container:: operations | ||||
|  | ||||
|         .. describe:: x == y | ||||
|  | ||||
|             Checks if two widgets are the same. | ||||
|  | ||||
|         .. describe:: x != y | ||||
|  | ||||
|             Checks if two widgets are not the same. | ||||
|  | ||||
|         .. describe:: str(x) | ||||
|  | ||||
|             Returns the widget's JSON URL. | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     id: :class:`int` | ||||
|         The guild's ID. | ||||
|     name: :class:`str` | ||||
|         The guild's name. | ||||
|     channels: Optional[List[:class:`WidgetChannel`]] | ||||
|         The accessible voice channels in the guild. | ||||
|     members: Optional[List[:class:`Member`]] | ||||
|         The online members in the server. Offline members | ||||
|         do not appear in the widget. | ||||
|     """ | ||||
|     __slots__ = ('_state', 'channels', '_invite', 'id', 'members', 'name') | ||||
|  | ||||
|     def __init__(self, *, state, data): | ||||
|         self._state = state | ||||
|         self._invite = data['instant_invite'] | ||||
|         self.name = data['name'] | ||||
|         self.id = int(data['id']) | ||||
|  | ||||
|         self.channels = [] | ||||
|         for channel in data.get('channels', []): | ||||
|             _id = int(channel['id']) | ||||
|             self.channels.append(WidgetChannel(id=_id, name=channel['name'], position=channel['position'])) | ||||
|  | ||||
|         self.members = [] | ||||
|         channels = {channel.id: channel for channel in self.channels} | ||||
|         for member in data.get('members', []): | ||||
|             connected_channel = _get_as_snowflake(member, 'channel_id') | ||||
|             if connected_channel: | ||||
|                 connected_channel = channels[connected_channel] | ||||
|  | ||||
|             self.members.append(WidgetMember(state=self._state, data=member, connected_channel=connected_channel)) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.json_url | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         return self.id == other.id | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<Widget id={0.id} name={0.name!r} invite={0.invite!r}>'.format(self) | ||||
|  | ||||
|     @property | ||||
|     def created_at(self): | ||||
|         """Returns the member's creation time in UTC.""" | ||||
|         return snowflake_time(self.id) | ||||
|  | ||||
|     @property | ||||
|     def json_url(self): | ||||
|         """The JSON URL of the widget.""" | ||||
|         return "https://discordapp.com/api/guilds/{0.id}/widget.json".format(self) | ||||
|  | ||||
|     async def fetch_invite(self, *, with_counts=True): | ||||
|         """|coro| | ||||
|  | ||||
|         Retrieves an :class:`Invite` from a invite URL or ID. | ||||
|         This is the same as :meth:`Client.get_invite`; the invite | ||||
|         code is abstracted away. | ||||
|  | ||||
|         Parameters | ||||
|         ----------- | ||||
|         with_counts: :class:`bool` | ||||
|             Whether to include count information in the invite. This fills the | ||||
|             :attr:`Invite.approximate_member_count` and :attr:`Invite.approximate_presence_count` | ||||
|             fields. | ||||
|  | ||||
|         Returns | ||||
|         -------- | ||||
|         :class:`Invite` | ||||
|             The invite from the URL/ID. | ||||
|         """ | ||||
|         if self._invite: | ||||
|             invite_id = resolve_invite(self._invite) | ||||
|             data = await self._state.http.get_invite(invite_id, with_counts=with_counts) | ||||
|             return Invite.from_incomplete(state=self._state, data=data) | ||||
		Reference in New Issue
	
	Block a user