Refactor folders

This commit is contained in:
KurimuzonAkuma 2023-12-01 18:38:45 +03:00
parent a4b92aabd1
commit e73f32f847
8 changed files with 346 additions and 171 deletions

View File

@ -248,7 +248,6 @@ def pyrogram_api():
toggle_forum_topics
delete_folder
export_folder_link
get_folder
get_folders
update_folder
""",
@ -684,10 +683,11 @@ def pyrogram_api():
folder="""
Folder
Folder.delete
Folder.update_peers
Folder.pin_chat
Folder.update
Folder.include_chat
Folder.exclude_chat
Folder.pin_chat
Folder.remove_chat
Folder.export_link
"""
)

View File

@ -40,7 +40,6 @@ from .get_chat_members_count import GetChatMembersCount
from .get_chat_online_count import GetChatOnlineCount
from .get_dialogs import GetDialogs
from .get_dialogs_count import GetDialogsCount
from .get_folder import GetFolder
from .get_folders import GetFolders
from .get_forum_topics import GetForumTopics
from .get_forum_topics_by_id import GetForumTopicsByID
@ -96,7 +95,6 @@ class Chats(
SetChatUsername,
SetChatPermissions,
GetDialogsCount,
GetFolder,
GetFolders,
GetForumTopics,
GetForumTopicsByID,

View File

@ -38,20 +38,27 @@ class ExportFolderLink:
# Export folder link
app.export_folder_link(123456789)
"""
folder = await self.get_folder(folder_id)
folder = await self.get_folders(folder_id)
if not folder:
return
peers = []
if folder.included_chats:
peers.extend(iter(folder.included_chats))
if folder.excluded_chats:
peers.extend(iter(folder.included_chats))
if folder.pinned_chats:
peers.extend(iter(folder.included_chats))
r = await self.invoke(
raw.functions.chatlists.ExportChatlistInvite(
chatlist=raw.types.InputChatlistDialogFilter(
filter_id=folder_id
),
chatlist=raw.types.InputChatlistDialogFilter(filter_id=folder_id),
title=folder.title,
peers=[await self.resolve_peer(i.id) for i in folder.pinned_peers] if folder.pinned_peers else []
+ [await self.resolve_peer(i.id) for i in folder.included_peers] if folder.included_peers else []
+ [await self.resolve_peer(i.id) for i in folder.excluded_peers] if folder.excluded_peers else [],
peers=[await self.resolve_peer(i.id) for i in peers],
)
)

View File

@ -1,45 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
import pyrogram
from pyrogram import types
class GetFolder:
async def get_folder(
self: "pyrogram.Client",
folder_id: int
) -> Optional["types.Folder"]:
"""Get a user's folder by id.
.. include:: /_includes/usable-by/users.rst
Returns:
:obj:`~pyrogram.types.Folder`: On success, the user's folder is returned.
Example:
.. code-block:: python
# Get folder by id
await app.get_folder(123456789)
"""
async for folder in self.get_folders():
if folder.id == folder_id:
return folder

View File

@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import AsyncGenerator, Optional
from typing import Union, List, Iterable
import pyrogram
from pyrogram import types, raw
@ -25,48 +25,66 @@ from pyrogram import types, raw
class GetFolders:
async def get_folders(
self: "pyrogram.Client",
) -> AsyncGenerator["types.Folder", None]:
"""Get a user's folders with chats sequentially.
folder_ids: Union[int, Iterable[int]] = None,
) -> Union["types.Folder", List["types.Folder"]]:
"""Get one or more folders by using folder identifiers.
.. include:: /_includes/usable-by/users.rst
Parameters:
folder_ids (``int`` | Iterable of ``int``, *optional*):
Pass a single folder identifier or an iterable of folder ids (as integers) to get the content of the
folders themselves.
By default all folders are returned.
Returns:
``Generator``: A generator yielding :obj:`~pyrogram.types.Folder` objects.
:obj:`~pyrogram.types.Folder` | List of :obj:`~pyrogram.types.Folder`: In case *folder_ids* was not
a list, a single folder is returned, otherwise a list of folders is returned.
Example:
.. code-block:: python
# Iterate through all folders
async for folder in app.get_folders():
print(folder.title)
# Get one folder
await app.get_folders(12345)
# Get more than one folders (list of folders)
await app.get_folders([12345, 12346])
# Get all folders
await app.get_folders()
"""
is_iterable = hasattr(folder_ids, "__iter__")
ids = list(folder_ids) if is_iterable else [folder_ids]
raw_folders = await self.invoke(raw.functions.messages.GetDialogFilters())
dialog_peers = []
for folder in raw_folders:
if not isinstance(folder, (raw.types.DialogFilter, raw.types.DialogFilterChatlist)):
continue
if isinstance(folder, (raw.types.DialogFilter, raw.types.DialogFilterChatlist)):
peers = folder.pinned_peers + folder.include_peers + getattr(folder, "exclude_peers", [])
input_peers = [raw.types.InputDialogPeer(peer=peer) for peer in peers] + [raw.types.InputDialogPeerFolder(folder_id=folder.id)]
peers = folder.pinned_peers + folder.include_peers + getattr(folder, "exclude_peers", [])
input_peers = [raw.types.InputDialogPeer(peer=peer) for peer in peers] + [raw.types.InputDialogPeerFolder(folder_id=folder.id)]
dialog_peers.extend(input_peers)
dialog_peers.extend(input_peers)
r = await self.invoke(raw.functions.messages.GetPeerDialogs(peers=dialog_peers))
users = {i.id: i for i in r.users}
chats = {i.id: i for i in r.chats}
peers = {**users, **chats}
folders = []
folders = types.List([])
for folder in raw_folders:
if not isinstance(folder, (raw.types.DialogFilter, raw.types.DialogFilterChatlist)):
continue
folders.append(types.Folder._parse(self, folder, peers))
if isinstance(folder, (raw.types.DialogFilter, raw.types.DialogFilterChatlist)):
folders.append(types.Folder._parse(self, folder, users, chats))
if not folders:
return
return None
for folder in folders:
yield folder
if folder_ids:
folders = types.List([folder for folder in folders if folder.id in ids])
if is_iterable:
return folders or None
else:
return folders[0] if folders else None
return folders

View File

@ -26,14 +26,14 @@ class UpdateFolder:
async def update_folder(
self: "pyrogram.Client",
folder_id: int,
title: str,
pinned_peers: Union[Union[int, str], List[Union[int, str]]],
included_peers: Union[Union[int, str], List[Union[int, str]]],
excluded_peers: Union[Union[int, str], List[Union[int, str]]],
included_chats: Union[Union[int, str], List[Union[int, str]]] = None,
excluded_chats: Union[Union[int, str], List[Union[int, str]]] = None,
pinned_chats: Union[Union[int, str], List[Union[int, str]]] = None,
title: str = None,
contacts: bool = None,
non_contacts: bool = None,
groups: bool = None,
broadcasts: bool = None,
channels: bool = None,
bots: bool = None,
exclude_muted: bool = None,
exclude_read: bool = None,
@ -44,6 +44,53 @@ class UpdateFolder:
.. include:: /_includes/usable-by/users.rst
Parameters:
folder_id (``int``):
Unique folder identifier.
included_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*):
Users or chats that should added in the folder
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
excluded_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*):
Users or chats that should excluded from the folder
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
pinned_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*):
Users or chats that should pinned in the folder
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
title (``str``, *optional*):
A folder title was changed to this value.
contacts (``bool``, *optional*):
Pass True if folder should contain contacts.
non_contacts (``bool``, *optional*):
Pass True if folder should contain non contacts.
groups (``bool``, *optional*):
Pass True if folder should contain groups.
channels (``bool``, *optional*):
Pass True if folder should contain channels.
bots (``bool``, *optional*):
Pass True if folder should contain bots.
exclude_muted (``bool``, *optional*):
Pass True if folder should exclude muted users.
exclude_archived (``bool``, *optional*):
Pass True if folder should exclude archived users.
emoji (``str``, *optional*):
Folder emoji.
Pass None to leave the folder icon as default.
Returns:
``bool``: True, on success.
@ -51,14 +98,14 @@ class UpdateFolder:
.. code-block:: python
# Delete folder
app.delete_folder(folder_id)
app.update_folder(folder_id, title="New folder", included_chats=["me"])
"""
if not isinstance(pinned_peers, list):
pinned_peers = [pinned_peers]
if not isinstance(included_peers, list):
included_peers = [included_peers]
if not isinstance(excluded_peers, list):
excluded_peers = [excluded_peers]
if not isinstance(included_chats, list):
included_chats = [included_chats]
if not isinstance(excluded_chats, list):
excluded_chats = [excluded_chats]
if not isinstance(pinned_chats, list):
pinned_chats = [pinned_chats]
r = await self.invoke(
raw.functions.messages.UpdateDialogFilter(
@ -68,20 +115,20 @@ class UpdateFolder:
title=title,
pinned_peers=[
await self.resolve_peer(user_id)
for user_id in pinned_peers
for user_id in pinned_chats
],
include_peers=[
await self.resolve_peer(user_id)
for user_id in included_peers
for user_id in included_chats
],
exclude_peers=[
await self.resolve_peer(user_id)
for user_id in excluded_peers
for user_id in excluded_chats
],
contacts=contacts,
non_contacts=non_contacts,
groups=groups,
broadcasts=broadcasts,
broadcasts=channels,
bots=bots,
exclude_muted=exclude_muted,
exclude_read=exclude_read,

View File

@ -344,9 +344,9 @@ class Chat(Object):
@staticmethod
def _parse_dialog(client, peer, users: dict, chats: dict):
if isinstance(peer, raw.types.PeerUser):
if isinstance(peer, (raw.types.PeerUser, raw.types.InputPeerUser)):
return Chat._parse_user_chat(client, users[peer.user_id])
elif isinstance(peer, raw.types.PeerChat):
elif isinstance(peer, (raw.types.PeerChat, raw.types.InputPeerChat)):
return Chat._parse_chat_chat(client, chats[peer.chat_id])
else:
return Chat._parse_channel_chat(client, chats[peer.channel_id])

View File

@ -21,8 +21,8 @@ from typing import List, Union
import pyrogram
from pyrogram import raw
from pyrogram import types
from pyrogram import utils
from ..object import Object
from ... import utils
class Folder(Object):
@ -35,15 +35,15 @@ class Folder(Object):
title (``str``):
The folder title.
pinned_peers (List of :obj:`~pyrogram.types.Chat`):
A list of pinned chats in folder.
included_peers (List of :obj:`~pyrogram.types.Chat`):
included_chats (List of :obj:`~pyrogram.types.Chat`, *optional*):
A list of included chats in folder.
excluded_peers (List of :obj:`~pyrogram.types.Chat`, *optional*):
excluded_chats (List of :obj:`~pyrogram.types.Chat`, *optional*):
A list of excluded chats in folder.
pinned_chats (List of :obj:`~pyrogram.types.Chat`, *optional*):
A list of pinned chats in folder.
contacts (``bool``, *optional*):
True, if the folder includes contacts.
@ -53,8 +53,8 @@ class Folder(Object):
groups (``bool``, *optional*):
True, if the folder includes groups.
broadcasts (``bool``, *optional*):
True, if the folder includes broadcasts.
channels (``bool``, *optional*):
True, if the folder includes channels.
bots (``bool``, *optional*):
True, if the folder includes bots.
@ -78,13 +78,13 @@ class Folder(Object):
client: "pyrogram.Client" = None,
id: int,
title: str,
pinned_peers: List["types.Chat"] = None,
included_peers: List["types.Chat"] = None,
excluded_peers: List["types.Chat"] = None,
included_chats: List["types.Chat"] = None,
excluded_chats: List["types.Chat"] = None,
pinned_chats: List["types.Chat"] = None,
contacts: bool = None,
non_contacts: bool = None,
groups: bool = None,
broadcasts: bool = None,
channels: bool = None,
bots: bool = None,
exclude_muted: bool = None,
exclude_read: bool = None,
@ -96,13 +96,13 @@ class Folder(Object):
self.id = id
self.title = title
self.pinned_peers = pinned_peers
self.included_peers = included_peers
self.excluded_peers = excluded_peers
self.included_chats = included_chats
self.excluded_chats = excluded_chats
self.pinned_chats = pinned_chats
self.contacts = contacts
self.non_contacts = non_contacts
self.groups = groups
self.broadcasts = broadcasts
self.channels = channels
self.bots = bots
self.exclude_muted = exclude_muted
self.exclude_read = exclude_read
@ -110,21 +110,32 @@ class Folder(Object):
self.emoji = emoji
self.has_my_invites = has_my_invites
@staticmethod
def _parse(client, folder: "raw.types.DialogFilter", peers) -> "Folder":
# TODO: Change types.Chat._parse to types.Dialog._parse
def _parse(client, folder: "raw.types.DialogFilter", users, chats) -> "Folder":
included_chats = []
excluded_chats = []
pinned_chats = []
for peer in folder.include_peers:
included_chats.append(types.Chat._parse_dialog(client, peer, users, chats))
if getattr(folder, "exclude_peers", None):
for peer in folder.exclude_peers:
excluded_chats.append(types.Chat._parse_dialog(client, peer, users, chats))
for peer in folder.pinned_peers:
pinned_chats.append(types.Chat._parse_dialog(client, peer, users, chats))
return Folder(
id=folder.id,
title=folder.title,
pinned_peers=types.List(types.Chat._parse_chat(client, peers.get(utils.get_input_peer_id(i), None)) for i in folder.pinned_peers) or None,
included_peers=types.List(types.Chat._parse_chat(client, peers.get(utils.get_input_peer_id(i), None)) for i in folder.include_peers) or None,
excluded_peers=types.List(types.Chat._parse_chat(client, peers.get(utils.get_input_peer_id(i), None)) for i in folder.exclude_peers) or None if getattr(folder, "exclude_peers", None) else None,
included_chats=types.List(included_chats) or None,
excluded_chats=types.List(excluded_chats) or None,
pinned_chats=types.List(pinned_chats) or None,
contacts=getattr(folder, "contacts", None),
non_contacts=getattr(folder, "non_contacts", None),
groups=getattr(folder, "groups", None),
broadcasts=getattr(folder, "broadcasts", None),
channels=getattr(folder, "broadcasts", None),
bots=getattr(folder, "bots", None),
exclude_muted=getattr(folder, "exclude_muted", None),
exclude_read=getattr(folder, "exclude_read", None),
@ -154,63 +165,110 @@ class Folder(Object):
return await self._client.delete_folder(self.id)
async def update_peers(self, pinned_peers: List[Union[int, str]], included_peers: List[Union[int, str]], excluded_peers: List[Union[int, str]]):
async def update(
self,
included_chats: List[Union[int, str]] = None,
excluded_chats: List[Union[int, str]] = None,
pinned_chats: List[Union[int, str]] = None,
title: str = None,
contacts: bool = None,
non_contacts: bool = None,
groups: bool = None,
channels: bool = None,
bots: bool = None,
exclude_muted: bool = None,
exclude_read: bool = None,
exclude_archived: bool = None,
emoji: str = None
):
"""Bound method *update_peers* of :obj:`~pyrogram.types.Folder`.
Use as a shortcut for:
.. code-block:: python
await client.update_folder(123456789, ...)
await client.update_folder(
folder_id,
title="New folder",
included_chats=["me"]
)
Example:
.. code-block:: python
await folder.update_peers(...)
await folder.update(included_chats=["me"])
Parameters:
included_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*):
Users or chats that should added in the folder
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
excluded_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*):
Users or chats that should excluded from the folder
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
pinned_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*):
Users or chats that should pinned in the folder
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
title (``str``, *optional*):
A folder title was changed to this value.
contacts (``bool``, *optional*):
Pass True if folder should contain contacts.
non_contacts (``bool``, *optional*):
Pass True if folder should contain non contacts.
groups (``bool``, *optional*):
Pass True if folder should contain groups.
channels (``bool``, *optional*):
Pass True if folder should contain channels.
bots (``bool``, *optional*):
Pass True if folder should contain bots.
exclude_muted (``bool``, *optional*):
Pass True if folder should exclude muted users.
exclude_archived (``bool``, *optional*):
Pass True if folder should exclude archived users.
emoji (``str``, *optional*):
Folder emoji.
Pass None to leave the folder icon as default.
Returns:
True on success.
"""
if not included_chats:
included_chats = [i.id for i in self.included_chats or []]
if not included_chats:
excluded_chats = [i.id for i in self.excluded_chats or []]
if not included_chats:
pinned_chats = [i.id for i in self.pinned_chats or []]
return await self._client.update_folder(
folder_id=self.id,
title=self.title,
pinned_peers=pinned_peers,
included_peers=included_peers,
excluded_peers=excluded_peers,
contacts=self.contacts,
non_contacts=self.non_contacts,
groups=self.groups,
broadcasts=self.broadcasts,
bots=self.bots,
exclude_muted=self.exclude_muted,
exclude_read=self.exclude_read,
exclude_archived=self.exclude_archived,
emoji=self.emoji
)
async def pin_chat(self, chat_id: Union[int, str]):
"""Bound method *pin_chat* of :obj:`~pyrogram.types.Folder`.
Use as a shortcut for:
.. code-block:: python
await client.update_folder(123456789, ...)
Example:
.. code-block:: python
await folder.pin_chat(chat_id)
Returns:
True on success.
"""
return await self.update_peers(
pinned_peers=[i.id for i in self.pinned_peers] if self.pinned_peers else [] + [chat_id],
included_peers=[i.id for i in self.included_peers] if self.included_peers else [] + [chat_id],
excluded_peers=[i.id for i in self.excluded_peers] if self.excluded_peers else [],
title=title or self.title,
included_chats=included_chats,
excluded_chats=excluded_chats,
pinned_chats=pinned_chats,
contacts=contacts or self.contacts,
non_contacts=non_contacts or self.non_contacts,
groups=groups or self.groups,
channels=channels or self.channels,
bots=bots or self.bots,
exclude_muted=exclude_muted or self.exclude_muted,
exclude_read=exclude_read or self.exclude_read,
exclude_archived=exclude_archived or self.exclude_archived,
emoji=emoji or self.emoji
)
async def include_chat(self, chat_id: Union[int, str]):
@ -220,21 +278,31 @@ class Folder(Object):
.. code-block:: python
await client.update_folder(123456789, ...)
await client.update_folder(
folder_id=123456789,
included_chats=[chat_id],
excluded_chats=[...],
pinned_chats=[...]
)
Example:
.. code-block:: python
await folder.include_chat(chat_id)
Parameters:
chat_id (``int`` | ``str``):
Unique identifier for the target chat or username of the target user/channel/supergroup
(in the format @username).
Returns:
True on success.
"""
return await self.update_peers(
pinned_peers=[i.id for i in self.pinned_peers] if self.pinned_peers else [],
included_peers=[i.id for i in self.included_peers] if self.included_peers else [] + [chat_id],
excluded_peers=[i.id for i in self.excluded_peers] if self.excluded_peers else [],
return await self.update(
included_chats=[i.id for i in self.included_chats or []] + [chat_id],
excluded_chats=[i.id for i in self.excluded_chats or []],
pinned_chats=[i.id for i in self.pinned_chats or []]
)
async def exclude_chat(self, chat_id: Union[int, str]):
@ -244,21 +312,103 @@ class Folder(Object):
.. code-block:: python
await client.update_folder(123456789, ...)
await client.update_folder(
folder_id=123456789,
included_chats=[...],
excluded_chats=[chat_id],
pinned_chats=[...]
)
Example:
.. code-block:: python
await folder.exclude_chat(chat_id)
Parameters:
chat_id (``int`` | ``str``):
Unique identifier for the target chat or username of the target user/channel/supergroup
(in the format @username).
Returns:
True on success.
"""
return await self.update_peers(
pinned_peers=[i.id for i in self.pinned_peers] if self.pinned_peers else [],
included_peers=[i.id for i in self.included_peers] if self.included_peers else [],
excluded_peers=[i.id for i in self.excluded_peers] if self.excluded_peers else [] + [chat_id],
return await self.update(
included_chats=[i.id for i in self.included_chats or []],
excluded_chats=[i.id for i in self.excluded_chats or []] + [chat_id],
pinned_chats=[i.id for i in self.pinned_chats or []],
)
async def pin_chat(self, chat_id: Union[int, str]):
"""Bound method *pin_chat* of :obj:`~pyrogram.types.Folder`.
Use as a shortcut for:
.. code-block:: python
await client.update_folder(
folder_id=123456789,
included_chats=[chat_id],
excluded_chats=[chat_id],
pinned_chats=[...]
)
Example:
.. code-block:: python
await folder.pin_chat(chat_id)
Parameters:
chat_id (``int`` | ``str``):
Unique identifier for the target chat or username of the target user/channel/supergroup
(in the format @username).
Returns:
True on success.
"""
return await self.update(
included_chats=[i.id for i in self.included_chats or []] + [chat_id],
excluded_chats=[i.id for i in self.excluded_chats or []],
pinned_chats=[i.id for i in self.pinned_chats or []] + [chat_id]
)
async def remove_chat(self, chat_id: Union[int, str]):
"""Bound method *remove_chat* of :obj:`~pyrogram.types.Folder`.
Remove chat from included, excluded and pinned chats.
Use as a shortcut for:
.. code-block:: python
await client.update_folder(
folder_id=123456789,
included_chats=[...],
excluded_chats=[...],
pinned_chats=[...]
)
Example:
.. code-block:: python
await folder.remove_chat(chat_id)
Parameters:
chat_id (``int`` | ``str``):
Unique identifier for the target chat or username of the target user/channel/supergroup
(in the format @username).
Returns:
True on success.
"""
peer = await self._client.resolve_peer(chat_id)
peer_id = utils.get_peer_id(peer)
return await self.update(
included_chats=[i.id for i in self.included_chats or [] if peer_id != i.id],
excluded_chats=[i.id for i in self.excluded_chats or [] if peer_id != i.id],
pinned_chats=[i.id for i in self.pinned_chats or [] if peer_id != i.id]
)
async def export_link(self):
@ -268,12 +418,12 @@ class Folder(Object):
.. code-block:: python
await client.export_link(123456789)
await client.export_folder_link(123456789)
Example:
.. code-block:: python
await folder.export_folder_link(chat_id)
await folder.export_link()
Returns:
``str``: On success, a link to the folder as string is returned.