From 6eeea6e97792f0cd3e3a4ac0ce336127fbc83e6e Mon Sep 17 00:00:00 2001 From: grey-cat-1908 Date: Sun, 27 Mar 2022 10:22:08 +0300 Subject: [PATCH] channel delete message, bulk delete, purge --- melisa/__init__.py | 2 +- melisa/core/http.py | 27 +++-- melisa/models/guild/channel.py | 178 +++++++++++++++++++++++++++++++-- 3 files changed, 189 insertions(+), 18 deletions(-) diff --git a/melisa/__init__.py b/melisa/__init__.py index d00d2a1..eee9584 100644 --- a/melisa/__init__.py +++ b/melisa/__init__.py @@ -2,7 +2,7 @@ # Full MIT License can be found in `LICENSE.txt` at the project root. from .client import * -from .models import Intents +from .models import Intents, Snowflake from .exceptions import * __package__ = "melisa" diff --git a/melisa/core/http.py b/melisa/core/http.py index 8aad56e..ba1b137 100644 --- a/melisa/core/http.py +++ b/melisa/core/http.py @@ -4,7 +4,7 @@ from __future__ import annotations import asyncio -from typing import Dict, Optional +from typing import Dict, Optional, Any from aiohttp import ClientSession, ClientResponse @@ -65,6 +65,7 @@ class HTTPClient: endpoint: str, *, _ttl: int = None, + headers: Optional[Dict[str, Any]] = None, params: Optional[Dict] = None, **kwargs, ) -> Optional[Dict]: @@ -80,7 +81,14 @@ class HTTPClient: url = f"{self.url}/{endpoint}" async with self.__aiohttp_session.request( - method, url, params=remove_none(params), **kwargs + method, + url, + params=remove_none(params), + headers={ + "Content-Type": "application/json", + **(remove_none(headers) or {}), + }, + **kwargs, ) as response: return await self.__handle_response( response, method, endpoint, _ttl=ttl, **kwargs @@ -100,6 +108,9 @@ class HTTPClient: self.__rate_limiter.save_response_bucket(endpoint, method, res.headers) if res.ok: + if res.status == 204: + return + return await res.json() exception = self.__http_exceptions.get(res.status) @@ -138,7 +149,9 @@ class HTTPClient: """ return await self.__send("GET", route, params=params) - async def post(self, route: str, data: Optional[Dict] = None) -> Optional[Dict]: + async def post( + self, route: str, *, headers: dict = None, data: Optional[Dict] = None + ) -> Optional[Dict]: """|coro| Sends a POST request to a Discord REST API endpoint. @@ -154,13 +167,9 @@ class HTTPClient: Optional[:class:`Dict`] JSON response from the Discord API. """ - return await self.__send( - "POST", - route, - json=data, - ) + return await self.__send("POST", route, json=data, headers=headers) - async def delete(self, route: str, headers: dict = None) -> Optional[Dict]: + async def delete(self, route: str, *, headers: dict = None) -> Optional[Dict]: """|coro| Sends a DELETE request to a Discord REST API endpoint. diff --git a/melisa/models/guild/channel.py b/melisa/models/guild/channel.py index 28b474f..11fff67 100644 --- a/melisa/models/guild/channel.py +++ b/melisa/models/guild/channel.py @@ -3,6 +3,7 @@ from __future__ import annotations +import asyncio from dataclasses import dataclass from enum import IntEnum from typing import List, Any, Optional, AsyncIterator, Union, Dict @@ -138,14 +139,21 @@ class TextChannel(Channel): Parameters ---------- - around : Optional[Union[:class:`int`, :class:`str`, :class:`Snowflake`]] - Get messages around this message ID. - before : Optional[Union[:class:`int`, :class:`str`, :class:`Snowflake`]] - Get messages before this message ID. - after : Optional[Union[:class:`int`, :class:`str`, :class:`Snowflake`]] - Get messages after this message ID. - limit : Optional[Union[:class:`int`, :class:`str`, :class:`Snowflake`]] + limit : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] Max number of messages to return (1-100). + around : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Get messages around this message ID. + before : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Get messages before this message ID. + after : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Get messages after this message ID. + + Raises + ------- + NotFoundError + If channel is not found. + ForbiddenError + You do not have proper permissions to do the actions required. Returns ------- @@ -153,6 +161,8 @@ class TextChannel(Channel): An iterator of messages. """ + # ToDo: Add check parameter + if limit is None: limit = 100 @@ -178,8 +188,160 @@ class TextChannel(Channel): before = raw_messages[-1]["id"] limit -= search_limit + async def fetch_message( + self, + message_id: Optional[Snowflake, int, str], + ) -> Dict[str, Any]: + """|coro| + Returns a specific message in the channel. + + Parameters + ---------- + message_id : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Id of message to fetch. + + Raises + ------- + NotFoundError + If message is not found. + ForbiddenError + You do not have proper permissions to do the actions required. + + Returns + ------- + Dict[:class:`str`, Any] + Message object. + """ + + message = await self._http.get( + f"/channels/{self.id}/messages/{message_id}", + ) + + return message + + async def bulk_delete_messages( + self, messages: List[Snowflake], *, reason: Optional[str] = None + ): + """|coro| + Delete multiple messages in a single request. + This method will not delete messages older than 2 weeks. + + Parameters + ---------- + messages: List[:class:`~.melisa.Snowflake`] + The list of message IDs to delete (2-100). + reason: Optional[:class:`str`] + The reason of the bulk delete messages operation. + + Raises + ------- + BadRequestError + if any message provided is older than that or if any duplicate message IDs are provided. + ForbiddenError + You do not have proper permissions to do the actions required. + (You must have `MANAGE_MESSAGES` permission) + """ + await self._http.post( + f"channels/{self.id}/messages/bulk-delete", + headers={"X-Audit-Log-Reason": reason}, + data={"messages": messages}, + ) + + async def delete_message( + self, message_id: Optional[Snowflake, str, int], *, reason: Optional[str] = None + ): + """|coro| + Deletes only one specified message. + + Parameters + ---------- + message_id: Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`] + Id of message to delete. + reason: Optional[:class:`str`] + The reason of the message delete operation. + + Raises + ------- + BadRequestError + Something is wrong with request, maybe specified parameters. + NotFoundError + If message is not found. + ForbiddenError + You do not have proper permissions to do the actions required. + (You must have `MANAGE_MESSAGES` permission) + """ + await self._http.delete( + f"channels/{self.id}/messages/{message_id}", + headers={"X-Audit-Log-Reason": reason}, + ) + + async def purge( + self, + limit: int = 50, + *, + before: Optional[Union[int, str, Snowflake]] = None, + after: Optional[Union[int, str, Snowflake]] = None, + around: Optional[Union[int, str, Snowflake]] = None, + reason: Optional[str] = None, + ): + """|coro| + Purges a list of messages that meet the criteria specified in parameters. + This method will not delete messages older than 2 weeks. + + Parameters + ---------- + limit : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Max number of messages to purge. + around : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Get messages around this message ID. + before : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Get messages before this message ID. + after : Optional[Union[:class:`int`, :class:`str`, :class:`~.melisa.Snowflake`]] + Get messages after this message ID. + reason: Optional[:class:`str`] + The reason of the channel purge operation. + + Raises + ------- + BadRequestError + if any message provided is older than that or if any duplicate message IDs are provided. + ForbiddenError + You do not have proper permissions to do the actions required. + (You must have `MANAGE_MESSAGES` permission) + """ + + iterator = self.history( + limit, + around=around, + before=before, + after=after, + ) + + message_ids = [] + count = 0 + + async for message in iterator: + message_ids.append(message["id"]) + count += 1 + + if count == 100: + print("abobatelecom") + await self.bulk_delete_messages(message_ids, reason=reason) + message_ids = [] + count = 0 + await asyncio.sleep(1) + + await asyncio.sleep(1) + + if count > 1: + await self.bulk_delete_messages(message_ids, reason=reason) + return + if count == 0: + await self.delete_message(message_ids[0], reason=reason) + return + # noinspection PyTypeChecker -channel_types_for_converting: Dict[ChannelType, Any] = { +channel_types_for_converting: Dict[ChannelType, Channel] = { ChannelType.GUILD_TEXT: TextChannel }