diff --git a/melisa/models/guild/member.py b/melisa/models/guild/member.py index 7503293..934c1a9 100644 --- a/melisa/models/guild/member.py +++ b/melisa/models/guild/member.py @@ -4,7 +4,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import List, Dict +from typing import List, Dict, Optional, Union from melisa.utils.timestamp import Timestamp from melisa.utils.snowflake import Snowflake @@ -120,3 +120,9 @@ class GuildMember(APIModelBase): self.guild_id = data.get("guild_id") return self + + async def timeout(self, *, duration: Optional[float] = None, + until: Optional[Timestamp] = None): + """ + + """ diff --git a/melisa/models/message/embed.py b/melisa/models/message/embed.py index 3074f4a..135400d 100644 --- a/melisa/models/message/embed.py +++ b/melisa/models/message/embed.py @@ -360,7 +360,7 @@ class Embed(APIModelBase): return self - def set_timestamp(self, time: Union[Timestamp, datetime]) -> Embed: + def set_timestamp(self, time: datetime) -> Embed: """Sets timestamp in the supported by discord format. Parameters diff --git a/melisa/models/message/message.py b/melisa/models/message/message.py index b5fcc3a..e0f44a6 100644 --- a/melisa/models/message/message.py +++ b/melisa/models/message/message.py @@ -300,7 +300,7 @@ class Message(APIModelBase): self.guild_id = ( Snowflake(data["guild_id"]) if data.get("guild_id") is not None else None ) - self.author = _member + self.author = GuildMember.from_dict(_member) self.content = data.get("content", "") self.timestamp = Timestamp.parse(data["timestamp"]) self.edited_timestamp = ( diff --git a/melisa/rest.py b/melisa/rest.py index 5e887ac..52b86e7 100644 --- a/melisa/rest.py +++ b/melisa/rest.py @@ -1,6 +1,7 @@ # Copyright MelisaDev 2022 - Present # Full MIT License can be found in `LICENSE.txt` at the project root. +import datetime from typing import Union, Optional, List, Dict, Any, AsyncIterator from aiohttp import FormData @@ -8,7 +9,7 @@ from aiohttp import FormData from .models.message import Embed, File, AllowedMentions, Message from .exceptions import EmbedFieldError from .core.http import HTTPClient -from .utils import json +from .utils import json, UNDEFINED from .utils.snowflake import Snowflake from .models.guild.guild import Guild from .models.user.user import User @@ -92,11 +93,11 @@ class RESTApp: return _choose_channel_type(data) async def delete_message( - self, - channel_id: Union[Snowflake, str, int], - message_id: Union[Snowflake, str, int], - *, - reason: Optional[str] = None, + self, + channel_id: Union[Snowflake, str, int], + message_id: Union[Snowflake, str, int], + *, + reason: Optional[str] = None, ): """|coro| @@ -125,18 +126,18 @@ class RESTApp: ) async def create_message( - self, - channel_id: Union[Snowflake, str, int], - content: str = None, - *, - tts: bool = False, - embed: Embed = None, - embeds: List[Embed] = None, - file: File = None, - files: List[File] = None, - allowed_mentions: AllowedMentions = None, - delete_after: int = None, - _client_allowed_mentions: AllowedMentions = None, + self, + channel_id: Union[Snowflake, str, int], + content: str = None, + *, + tts: bool = False, + embed: Embed = None, + embeds: List[Embed] = None, + file: File = None, + files: List[File] = None, + allowed_mentions: AllowedMentions = None, + delete_after: int = None, + _client_allowed_mentions: AllowedMentions = None, ) -> Message: """|coro| @@ -220,13 +221,13 @@ class RESTApp: return message_data async def get_channel_messages_history( - self, - channel_id: Union[Snowflake, str, int], - limit: int = 50, - *, - before: Optional[Snowflake] = None, - after: Optional[Snowflake] = None, - around: Optional[Snowflake] = None, + self, + channel_id: Union[Snowflake, str, int], + limit: int = 50, + *, + before: Optional[Snowflake] = None, + after: Optional[Snowflake] = None, + around: Optional[Snowflake] = None, ) -> AsyncIterator[Message]: """|coro| @@ -297,9 +298,9 @@ class RESTApp: limit -= search_limit async def fetch_message( - self, - channel_id: Union[Snowflake, int, str], - message_id: Union[Snowflake, int, str], + self, + channel_id: Union[Snowflake, int, str], + message_id: Union[Snowflake, int, str], ) -> Message: """|coro| @@ -334,7 +335,7 @@ class RESTApp: return Message.from_dict(message) async def fetch_channel_pins( - self, channel_id: Union[Snowflake, int, str] + self, channel_id: Union[Snowflake, int, str] ) -> AsyncIterator[Message]: """|coro| @@ -362,3 +363,90 @@ class RESTApp: for message in messages: yield Message.from_dict(message) + + async def modify_guild_member( + self, + guild_id: Union[Snowflake, str, int], + user_id: Union[Snowflake, str, int], + *, + nick: Optional[str] = UNDEFINED, + roles: Optional[List[Snowflake]] = UNDEFINED, + is_mute: Optional[bool] = UNDEFINED, + is_deaf: Optional[bool] = UNDEFINED, + voice_channel_id: Optional[Snowflake] = UNDEFINED, + communication_disabled_until: Optional[datetime.datetime] = UNDEFINED, + reason: Optional[str] = None, + ): + """|coro| + + [**REST API**] Modify attributes of a guild member. + + Parameters + ---------- + guild_id: Union[:class:`int`, :class:`str`, :class:`~.melisa.utils.snowflake.Snowflake`] + Id of guild where we will modify member + user_id: Union[:class:`int`, :class:`str`, :class:`~.melisa.utils.snowflake.Snowflake`] + Id of user to operate with. + nick: Optional[:class:`str`] + Value to set user's nickname to. + + **Required permissions:** ``MANAGE_NICKNAMES`` + roles: Optional[List[:class:`~.melisa.utils.snowflake.Snowflake`]] + List of role ids the member is assigned + + **Required permissions:** ``MANAGE_ROLES`` + is_mute + Whether the user is muted in voice channels. + + **Required permissions:** ``MUTE_MEMBERS`` + is_deaf + Whether the user is deafened in voice channels. + + **Required permissions:** ``DEAFEN_MEMBERS`` + voice_channel_id: Optional[:class:`~.melisa.utils.snowflake.Snowflake`] + Id of channel to move user to (if they are connected to voice) + + **Required permissions:** ``MOVE_MEMBERS`` + communication_disabled_until: Optional[:class:`~melisa.utils.timestamp.Timestamp`] + When the user's timeout will expire and the user will be able to communicate + in the guild again (up to 28 days in the future), + set to ``None`` to remove timeout. + + Will throw a 403 error if the user has the ``ADMINISTRATOR`` permission + or is the owner of the guild + + **Required permissions:** ``MODERATE_MEMBERS`` + reason: Optional[:class:`str`] + The reason of the message delete operation. + + Raises + ------- + HTTPException + The request to perform the action failed with other http exception. + ForbiddenError + You do not have proper permissions to do the actions required. + BadRequestError + You provided a wrong type of argument, or you set ``is_deaf``, + ``is_mute`` when user is not in the channel + """ + + data = {} + + if nick is not UNDEFINED: + data["nick"] = nick + if roles is not UNDEFINED: + data["roles"] = roles + if is_mute is not UNDEFINED: + data["mute"] = is_mute + if is_deaf is not UNDEFINED: + data["deaf"] = is_deaf + if voice_channel_id is not UNDEFINED: + data["channel_id"] = voice_channel_id + if communication_disabled_until is not UNDEFINED: + data["communication_disabled_until"] = communication_disabled_until.isoformat() + + await self._http.patch( + f"guilds/{guild_id}/members/{user_id}", + data=data, + headers={"X-Audit-Log-Reason": reason}, + )