Compare commits

...

18 commits

Author SHA1 Message Date
wulan17
8074557922
pyrofork: Bump version to 2.3.59
Some checks failed
Pyrofork / build (macos-latest, 3.10) (push) Has been cancelled
Pyrofork / build (macos-latest, 3.11) (push) Has been cancelled
Pyrofork / build (macos-latest, 3.12) (push) Has been cancelled
Pyrofork / build (macos-latest, 3.13) (push) Has been cancelled
Pyrofork / build (macos-latest, 3.9) (push) Has been cancelled
Pyrofork / build (ubuntu-latest, 3.10) (push) Has been cancelled
Pyrofork / build (ubuntu-latest, 3.11) (push) Has been cancelled
Pyrofork / build (ubuntu-latest, 3.12) (push) Has been cancelled
Pyrofork / build (ubuntu-latest, 3.13) (push) Has been cancelled
Pyrofork / build (ubuntu-latest, 3.9) (push) Has been cancelled
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:53:11 +07:00
wulan17
7562d04596
pyrofork: docs: text-formatting: Add missings text format
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:10 +07:00
wulan17
149dd4a708
pyrofork: docs: text-formatting: Move up HTML section
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:10 +07:00
wulan17
4926eda4c5
pyrofork: docs: Add warning regarding markdown text formatting
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:10 +07:00
wulan17
c4b587b493
pyrofork: parser: markdown: Check if PRE is inside blockquote before unparsing it
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:09 +07:00
wulan17
1108c52f53
pyrofork: Add support for custom emoji in markdown unparser
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:09 +07:00
wulan17
fc84041397
pyrofork: Add offset_{date,id,topic} parameters to get_forum_topics method
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:09 +07:00
wulan17
71c39b8e6f
pyrofork: Add support for multi-line blockquote in markdown unparser
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:09 +07:00
wulan17
bec31032cc
pyrofork: Adapt markdown unparser from telethon
* The problem with current implementation is when we have nested markdown inside a url the markdown order is messed up.
for example link with bold text will be unparsed like this [**github](https://github.com**).

Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:08 +07:00
wulan17
5c9470fd4f
pyrofork: Fix nested url markdown parsing
* The problem with current implepementation is when we add another markdown inside an url markdown will not be parsed.
for example we add bold (**) markdown inside an url markdown, the url text show as `**text**` instead of making the text bold.

Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:08 +07:00
KurimuzonAkuma
13681302a0
Pyrofork: Add delete_chat_history method
Signed-off-by: Yasir Aris <git@yasir.id>
2025-03-08 20:52:08 +07:00
Yasir Aris M
c31d41e3d3
Pyrofork: Fix video cover
Signed-off-by: Yasir Aris <git@yasir.id>
2025-03-08 20:52:07 +07:00
Yasir Aris M
8528eea303
Pyrofork: Fix get_chat_gifts_count description
Signed-off-by: Yasir Aris <git@yasir.id>
2025-03-08 20:52:07 +07:00
Yasir Aris M
39cd79794e
Pyrofork: Update get_chat_gifts description
Signed-off-by: Yasir Aris <git@yasir.id>
2025-03-08 20:52:07 +07:00
KurimuzonAkuma
ea8ff2806f
Pyrofork: Add message.content property
Signed-off-by: Yasir Aris <git@yasir.id>
2025-03-08 20:52:07 +07:00
wulan17
e33a9d95df
pyrofork: Update API schema to Layer 199
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:06 +07:00
wulan17
ee3e9002fc
pyrofork: Add get_chat_forum_topics_count method
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:52:06 +07:00
wulan17
4e200e2a5d
pyrofork: Add ForumTopicDeleted
Signed-off-by: wulan17 <wulan17@nusantararom.org>
2025-03-08 20:51:58 +07:00
19 changed files with 548 additions and 175 deletions

View file

@ -435,7 +435,7 @@ updateStarsBalance#4e80a379 balance:StarsAmount = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update; updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update; updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update; updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#51ca7aec private:Bool = Update; updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1886,7 +1886,7 @@ starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption; starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
starGift#2cc73c8 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long = StarGift; starGift#2cc73c8 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long = StarGift;
starGiftUnique#f2fe7e4a flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int = StarGift; starGiftUnique#5c62d151 flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string = StarGift;
payments.starGiftsNotModified#a388a368 = payments.StarGifts; payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts; payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
@ -1943,6 +1943,10 @@ inputSavedStarGiftChat#f101aa7f peer:InputPeer saved_id:long = InputSavedStarGif
payments.starGiftWithdrawalUrl#84aa3a9c url:string = payments.StarGiftWithdrawalUrl; payments.starGiftWithdrawalUrl#84aa3a9c url:string = payments.StarGiftWithdrawalUrl;
paidReactionPrivacyDefault#206ad49e = PaidReactionPrivacy;
paidReactionPrivacyAnonymous#1f0c1ad9 = PaidReactionPrivacy;
paidReactionPrivacyPeer#dc6cfcf0 peer:InputPeer = PaidReactionPrivacy;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1955,6 +1959,7 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X; invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X; invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X; invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X;
invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#aac7b717 flags:# no_joined_notifications:flags.0?true phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization; auth.signUp#aac7b717 flags:# no_joined_notifications:flags.0?true phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
@ -2340,8 +2345,8 @@ messages.editFactCheck#589ee75 peer:InputPeer msg_id:int text:TextWithEntities =
messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates; messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates;
messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCheck>; messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCheck>;
messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult; messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendPaidReaction#9dd6a67b flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?Bool = Updates; messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates;
messages.togglePaidReactionPrivacy#849ad397 peer:InputPeer msg_id:int private:Bool = Bool; messages.togglePaidReactionPrivacy#435885b5 peer:InputPeer msg_id:int private:PaidReactionPrivacy = Bool;
messages.getPaidReactionPrivacy#472455aa = Updates; messages.getPaidReactionPrivacy#472455aa = Updates;
messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool; messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool;
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool; messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
@ -2432,7 +2437,7 @@ channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint addr
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
channels.getInactiveChannels#11e831ee = messages.InactiveChats; channels.getInactiveChannels#11e831ee = messages.InactiveChats;
channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; channels.convertToGigagroup#b290c69 channel:InputChannel = Updates;
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers; channels.getSendAs#e785a43f flags:# for_paid_reactions:flags.0?true peer:InputPeer = channels.SendAsPeers;
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory; channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates; channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates; channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
@ -2657,4 +2662,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo; fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;
// LAYER 198 // LAYER 199

View file

@ -223,6 +223,7 @@ def pyrogram_api():
transcribe_audio transcribe_audio
translate_message_text translate_message_text
start_bot start_bot
delete_chat_history
""", """,
chats=""" chats="""
Chats Chats
@ -253,6 +254,7 @@ def pyrogram_api():
get_folders get_folders
get_forum_topics get_forum_topics
get_forum_topics_by_id get_forum_topics_by_id
get_forum_topics_count
set_chat_username set_chat_username
archive_chats archive_chats
unarchive_chats unarchive_chats
@ -553,6 +555,7 @@ def pyrogram_api():
ForumTopicCreated ForumTopicCreated
ForumTopicEdited ForumTopicEdited
ForumTopicClosed ForumTopicClosed
ForumTopicDeleted
ForumTopicReopened ForumTopicReopened
GeneralTopicHidden GeneralTopicHidden
GeneralTopicUnhidden GeneralTopicUnhidden

View file

@ -37,68 +37,16 @@ list of the basic styles currently supported by Pyrofork.
- spoiler - spoiler
- `text URL <https://pyrogram.org>`_ - `text URL <https://pyrogram.org>`_
- `user text mention <tg://user?id=123456789>`_ - `user text mention <tg://user?id=123456789>`_
- :emoji:`🔥`
- ``inline fixed-width code`` - ``inline fixed-width code``
- .. code-block:: text - .. code-block:: text
pre-formatted pre-formatted
fixed-width fixed-width
code block code block
- > Quoted text
Markdown Style - > Quoted text with collapse/expand button
--------------
To strictly use this mode, pass :obj:`~pyrogram.enums.ParseMode.MARKDOWN` to the *parse_mode* parameter when using
:meth:`~pyrogram.Client.send_message`. Use the following syntax in your message:
.. code-block:: text
**bold**
__italic__
--underline--
~~strike~~
||spoiler||
[text URL](https://pyrogram.org/)
[text user mention](tg://user?id=123456789)
`inline fixed-width code`
```
pre-formatted
fixed-width
code block
```
> Quoted text
**Example**:
.. code-block:: python
from pyrogram import enums
await app.send_message(
"me",
(
"**bold**, "
"__italic__, "
"--underline--, "
"~~strike~~, "
"||spoiler||, "
"[URL](https://pyrogram.org), "
"`code`, "
"```"
"for i in range(10):\n"
" print(i)"
"```"
),
parse_mode=enums.ParseMode.MARKDOWN
)
HTML Style HTML Style
---------- ----------
@ -122,10 +70,10 @@ To strictly use this mode, pass :obj:`~pyrogram.enums.HTML` to the *parse_mode*
<a href="tg://user?id=123456789">inline mention</a> <a href="tg://user?id=123456789">inline mention</a>
<code>inline fixed-width code</code>
<emoji id="12345678901234567890">🔥</emoji> <emoji id="12345678901234567890">🔥</emoji>
<code>inline fixed-width code</code>
<pre> <pre>
pre-formatted pre-formatted
fixed-width fixed-width
@ -134,6 +82,8 @@ To strictly use this mode, pass :obj:`~pyrogram.enums.HTML` to the *parse_mode*
<blockquote>Quoted text</blockquote> <blockquote>Quoted text</blockquote>
<blockquote expandable>Quoted text with collapse/expand button</blockquote>
**Example**: **Example**:
.. code-block:: python .. code-block:: python
@ -178,6 +128,72 @@ To strictly use this mode, pass :obj:`~pyrogram.enums.HTML` to the *parse_mode*
&lt;my text&gt; &lt;my text&gt;
Markdown Style
--------------
.. warning::
The Markdown style is not recommended for complex text formatting.
If you want to use complex text formatting such as nested entities, overlapping entities use the HTML style instead.
To strictly use this mode, pass :obj:`~pyrogram.enums.ParseMode.MARKDOWN` to the *parse_mode* parameter when using
:meth:`~pyrogram.Client.send_message`. Use the following syntax in your message:
.. code-block:: text
**bold**
__italic__
--underline--
~~strike~~
||spoiler||
[text URL](https://pyrogram.org/)
[text user mention](tg://user?id=123456789)
![🔥](tg://emoji?id=12345678901234567890)
`inline fixed-width code`
```
pre-formatted
fixed-width
code block
```
> Quoted text
**> Quoted text with collapse/expand button
**Example**:
.. code-block:: python
from pyrogram import enums
await app.send_message(
"me",
(
"**bold**, "
"__italic__, "
"--underline--, "
"~~strike~~, "
"||spoiler||, "
"[URL](https://pyrogram.org), "
"`code`, "
"```"
"for i in range(10):\n"
" print(i)"
"```"
),
parse_mode=enums.ParseMode.MARKDOWN
)
Different Styles Different Styles
---------------- ----------------
@ -230,18 +246,18 @@ strike` styles, and you can still combine both Markdown and HTML together.
Here there are some example texts you can try sending: Here there are some example texts you can try sending:
**Markdown**:
- ``**bold, --underline--**``
- ``**bold __italic --underline ~~strike~~--__**``
- ``**bold __and** italic__``
**HTML**: **HTML**:
- ``<b>bold, <u>underline</u></b>`` - ``<b>bold, <u>underline</u></b>``
- ``<b>bold <i>italic <u>underline <s>strike</s></u></i></b>`` - ``<b>bold <i>italic <u>underline <s>strike</s></u></i></b>``
- ``<b>bold <i>and</b> italic</i>`` - ``<b>bold <i>and</b> italic</i>``
**Markdown (Not Recommended)**:
- ``**bold, --underline--**``
- ``**bold __italic --underline ~~strike~~--__**``
- ``**bold __and** italic__``
**Combined**: **Combined**:
- ``--you can combine <i>HTML</i> with **Markdown**--`` - ``--you can combine <i>HTML</i> with **Markdown**--``

View file

@ -18,7 +18,7 @@
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>. # along with Pyrofork. If not, see <http://www.gnu.org/licenses/>.
__fork_name__ = "PyroFork" __fork_name__ = "PyroFork"
__version__ = "2.3.58" __version__ = "2.3.59"
__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)" __license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)"
__copyright__ = "Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>" __copyright__ = "Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>"

View file

@ -50,6 +50,7 @@ from .get_dialogs_count import GetDialogsCount
from .get_folders import GetFolders from .get_folders import GetFolders
from .get_forum_topics import GetForumTopics from .get_forum_topics import GetForumTopics
from .get_forum_topics_by_id import GetForumTopicsByID from .get_forum_topics_by_id import GetForumTopicsByID
from .get_forum_topics_count import GetForumTopicsCount
from .get_send_as_chats import GetSendAsChats from .get_send_as_chats import GetSendAsChats
from .join_chat import JoinChat from .join_chat import JoinChat
from .leave_chat import LeaveChat from .leave_chat import LeaveChat
@ -98,6 +99,7 @@ class Chats(
GetFolders, GetFolders,
GetForumTopics, GetForumTopics,
GetForumTopicsByID, GetForumTopicsByID,
GetForumTopicsCount,
ArchiveChats, ArchiveChats,
UnarchiveChats, UnarchiveChats,
CreateGroup, CreateGroup,

View file

@ -32,7 +32,10 @@ class GetForumTopics:
async def get_forum_topics( async def get_forum_topics(
self: "pyrogram.Client", self: "pyrogram.Client",
chat_id: Union[int, str], chat_id: Union[int, str],
limit: int = 0 limit: int = 0,
offset_date: int = 0,
offset_id: int = 0,
offset_topic: int = 0
) -> Optional[AsyncGenerator["types.ForumTopic", None]]: ) -> Optional[AsyncGenerator["types.ForumTopic", None]]:
"""Get one or more topic from a chat. """Get one or more topic from a chat.
@ -46,6 +49,15 @@ class GetForumTopics:
limit (``int``, *optional*): limit (``int``, *optional*):
Limits the number of topics to be retrieved. Limits the number of topics to be retrieved.
offset_date (``int``, *optional*):
Date of the last message of the last found topic.
offset_id (``int``, *optional*):
ID of the last message of the last found topic.
offset_topic (``int``, *optional*):
ID of the last found topic.
Returns: Returns:
``Generator``: On success, a generator yielding :obj:`~pyrogram.types.ForumTopic` objects is returned. ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.ForumTopic` objects is returned.
@ -62,7 +74,7 @@ class GetForumTopics:
peer = await self.resolve_peer(chat_id) peer = await self.resolve_peer(chat_id)
rpc = raw.functions.channels.GetForumTopics(channel=peer, offset_date=0, offset_id=0, offset_topic=0, limit=limit) rpc = raw.functions.channels.GetForumTopics(channel=peer, offset_date=offset_date, offset_id=offset_id, offset_topic=offset_topic, limit=limit)
r = await self.invoke(rpc, sleep_threshold=-1) r = await self.invoke(rpc, sleep_threshold=-1)

View file

@ -0,0 +1,63 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>
#
# This file is part of Pyrofork.
#
# Pyrofork 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.
#
# Pyrofork 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 Pyrofork. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import Union, Optional, AsyncGenerator
import pyrogram
from pyrogram import raw
from pyrogram import types
log = logging.getLogger(__name__)
class GetForumTopicsCount:
async def get_forum_topics_count(
self: "pyrogram.Client",
chat_id: Union[int, str]
) -> Optional[AsyncGenerator["types.ForumTopic", None]]:
"""Get forum topics count from a chat.
.. include:: /_includes/usable-by/users.rst
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
You can also use chat public link in form of *t.me/<username>* (str).
Returns:
``int``: On success, the count of forum topics is returned.
Example:
.. code-block:: python
# get all forum topics count
app.get_forum_topics_count(chat_id)
Raises:
ValueError: In case of invalid arguments.
"""
peer = await self.resolve_peer(chat_id)
rpc = raw.functions.channels.GetForumTopics(channel=peer, offset_date=0, offset_id=0, offset_topic=0, limit=0)
r = await self.invoke(rpc, sleep_threshold=-1)
return r.count

View file

@ -19,6 +19,7 @@
from .copy_media_group import CopyMediaGroup from .copy_media_group import CopyMediaGroup
from .copy_message import CopyMessage from .copy_message import CopyMessage
from .delete_chat_history import DeleteChatHistory
from .delete_messages import DeleteMessages from .delete_messages import DeleteMessages
from .delete_scheduled_messages import DeleteScheduledMessages from .delete_scheduled_messages import DeleteScheduledMessages
from .download_media import DownloadMedia from .download_media import DownloadMedia
@ -78,6 +79,7 @@ from .transcribe_audio import TranscribeAudio
from .translate_text import TranslateText from .translate_text import TranslateText
class Messages( class Messages(
DeleteChatHistory,
DeleteMessages, DeleteMessages,
DeleteScheduledMessages, DeleteScheduledMessages,
EditMessageCaption, EditMessageCaption,

View file

@ -0,0 +1,102 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>
#
# This file is part of Pyrofork.
#
# Pyrofork 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.
#
# Pyrofork 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 Pyrofork. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
import logging
from typing import Union
import pyrogram
from pyrogram import raw
from pyrogram import utils
log = logging.getLogger(__name__)
class DeleteChatHistory:
async def delete_chat_history(
self: "pyrogram.Client",
chat_id: Union[int, str],
max_id: int = 0,
revoke: bool = None,
just_clear = None,
min_date: datetime = None,
max_date: datetime = None,
) -> int:
"""Delete the history of a chat.
.. include:: /_includes/usable-by/users.rst
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
max_id (``int``, *optional*):
Maximum ID of message to delete.
revoke (``bool``, *optional*):
Deletes messages history for everyone.
Required ``True`` if using in channel.
just_clear (``bool``, *optional*):
If True, clear history for the current user, without actually removing chat.
For private and simple group chats only.
min_date (:py:obj:`~datetime.datetime`, *optional*):
Delete all messages newer than this time.
For private and simple group chats only.
max_date (:py:obj:`~datetime.datetime`, *optional*):
Delete all messages older than this time.
For private and simple group chats only.
Returns:
``int``: Amount of affected messages
Example:
.. code-block:: python
# Delete all messages in channel
await app.delete_chat_history(chat_id, revoke=True)
"""
peer = await self.resolve_peer(chat_id)
if isinstance(peer, raw.types.InputPeerChannel):
r = await self.invoke(
raw.functions.channels.DeleteHistory(
channel=raw.types.InputChannel(
channel_id=peer.channel_id,
access_hash=peer.access_hash
),
max_id=max_id,
for_everyone=revoke
)
)
else:
r = await self.invoke(
raw.functions.messages.DeleteHistory(
peer=peer,
max_id=max_id,
just_clear=just_clear,
revoke=revoke,
min_date=utils.datetime_to_timestamp(min_date),
max_date=utils.datetime_to_timestamp(max_date)
)
)
return len(r.updates[0].messages) if isinstance(peer, raw.types.InputPeerChannel) else r.pts_count

View file

@ -23,10 +23,7 @@ from datetime import datetime
from typing import Union, BinaryIO, List, Optional, Callable from typing import Union, BinaryIO, List, Optional, Callable
import pyrogram import pyrogram
from pyrogram import StopTransmission, enums from pyrogram import StopTransmission, enums, raw, types, utils
from pyrogram import raw
from pyrogram import types
from pyrogram import utils
from pyrogram.errors import FilePartMissing from pyrogram.errors import FilePartMissing
from pyrogram.file_id import FileType from pyrogram.file_id import FileType
@ -55,7 +52,7 @@ class SendVideo:
reply_to_chat_id: Union[int, str] = None, reply_to_chat_id: Union[int, str] = None,
quote_text: str = None, quote_text: str = None,
quote_entities: List["types.MessageEntity"] = None, quote_entities: List["types.MessageEntity"] = None,
cover: Optional[Union[str, "io.BytesIO"]] = None, cover: Union[str, BinaryIO] = None,
start_timestamp: int = None, start_timestamp: int = None,
schedule_date: datetime = None, schedule_date: datetime = None,
protect_content: bool = None, protect_content: bool = None,
@ -161,8 +158,12 @@ class SendVideo:
List of special entities that appear in quote_text, which can be specified instead of *parse_mode*. List of special entities that appear in quote_text, which can be specified instead of *parse_mode*.
for reply_to_message only. for reply_to_message only.
cover (``str`` | :obj:`io.BytesIO`, *optional*): cover (``str`` | ``BinaryIO``, *optional*):
Cover of the video; pass None to skip cover uploading. Video cover.
Pass a file_id as string to attach a photo that exists on the Telegram servers,
pass a HTTP URL as a string for Telegram to get a video from the Internet,
pass a file path as string to upload a new photo civer that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
start_timestamp (``int``, *optional*): start_timestamp (``int``, *optional*):
Timestamp from which the video playing must start, in seconds. Timestamp from which the video playing must start, in seconds.
@ -224,6 +225,9 @@ class SendVideo:
# Send self-destructing video # Send self-destructing video
await app.send_video("me", "video.mp4", ttl_seconds=10) await app.send_video("me", "video.mp4", ttl_seconds=10)
# Add video_cover to the video
await app.send_video(channel_id, "video.mp4", video_cover="coverku.jpg")
# Keep track of the progress while uploading # Keep track of the progress while uploading
async def progress(current, total): async def progress(current, total):
print(f"{current * 100 / total:.1f}%") print(f"{current * 100 / total:.1f}%")
@ -231,6 +235,9 @@ class SendVideo:
await app.send_video("me", "video.mp4", progress=progress) await app.send_video("me", "video.mp4", progress=progress)
""" """
file = None file = None
vidcover_file = None
vidcover_media = None
peer = await self.resolve_peer(chat_id)
reply_to = await utils.get_reply_to( reply_to = await utils.get_reply_to(
client=self, client=self,
@ -245,6 +252,45 @@ class SendVideo:
) )
try: try:
if cover is not None:
if isinstance(cover, str):
if os.path.isfile(cover):
vidcover_media = await self.invoke(
raw.functions.messages.UploadMedia(
peer=peer,
media=raw.types.InputMediaUploadedPhoto(
file=await self.save_file(cover)
)
)
)
elif re.match("^https?://", cover):
vidcover_media = await self.invoke(
raw.functions.messages.UploadMedia(
peer=peer,
media=raw.types.InputMediaPhotoExternal(
url=cover
)
)
)
else:
vidcover_file = utils.get_input_media_from_file_id(cover, FileType.PHOTO).id
else:
vidcover_media = await self.invoke(
raw.functions.messages.UploadMedia(
peer=peer,
media=raw.types.InputMediaUploadedPhoto(
file=await self.save_file(cover)
)
)
)
if vidcover_media:
vidcover_file = raw.types.InputPhoto(
id=vidcover_media.photo.id,
access_hash=vidcover_media.photo.access_hash,
file_reference=vidcover_media.photo.file_reference
)
if isinstance(video, str): if isinstance(video, str):
if os.path.isfile(video): if os.path.isfile(video):
thumb = await self.save_file(thumb) thumb = await self.save_file(thumb)
@ -264,7 +310,7 @@ class SendVideo:
), ),
raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(video)) raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(video))
], ],
video_cover=await self.save_file(cover) if cover else None, video_cover=vidcover_file,
video_timestamp=start_timestamp video_timestamp=start_timestamp
) )
elif re.match("^https?://", video): elif re.match("^https?://", video):
@ -272,7 +318,7 @@ class SendVideo:
url=video, url=video,
ttl_seconds=ttl_seconds, ttl_seconds=ttl_seconds,
spoiler=has_spoiler, spoiler=has_spoiler,
video_cover=await self.save_file(cover) if cover else None, video_cover=vidcover_file,
video_timestamp=start_timestamp video_timestamp=start_timestamp
) )
else: else:
@ -296,14 +342,14 @@ class SendVideo:
), ),
raw.types.DocumentAttributeFilename(file_name=file_name or video.name) raw.types.DocumentAttributeFilename(file_name=file_name or video.name)
], ],
video_cover=await self.save_file(cover) if cover else None, video_cover=vidcover_file,
video_timestamp=start_timestamp video_timestamp=start_timestamp
) )
while True: while True:
try: try:
rpc = raw.functions.messages.SendMedia( rpc = raw.functions.messages.SendMedia(
peer=await self.resolve_peer(chat_id), peer=peer,
media=media, media=media,
silent=disable_notification or None, silent=disable_notification or None,
reply_to=reply_to, reply_to=reply_to,

View file

@ -35,7 +35,7 @@ class GetChatGifts:
limit: int = 0, limit: int = 0,
offset: str = "" offset: str = ""
): ):
"""Get user star gifts. """Get all gifts owned by specified chat.
.. include:: /_includes/usable-by/users.rst .. include:: /_includes/usable-by/users.rst

View file

@ -35,7 +35,7 @@ class GetChatGiftsCount:
exclude_limited: Optional[bool] = None, exclude_limited: Optional[bool] = None,
exclude_upgraded: Optional[bool] = None exclude_upgraded: Optional[bool] = None
) -> int: ) -> int:
"""Get the total count of star gifts of specified user. """Get the total count of owned gifts of specified chat.
.. include:: /_includes/usable-by/users.rst .. include:: /_includes/usable-by/users.rst

View file

@ -37,7 +37,7 @@ PRE_DELIM = "```"
BLOCKQUOTE_DELIM = ">" BLOCKQUOTE_DELIM = ">"
BLOCKQUOTE_EXPANDABLE_DELIM = "**>" BLOCKQUOTE_EXPANDABLE_DELIM = "**>"
MARKDOWN_RE = re.compile(r"({d})|(!?)\[(.+?)\]\((.+?)\)".format( MARKDOWN_RE = re.compile(r"({d})".format(
d="|".join( d="|".join(
["".join(i) for i in [ ["".join(i) for i in [
[rf"\{j}" for j in i] [rf"\{j}" for j in i]
@ -52,6 +52,7 @@ MARKDOWN_RE = re.compile(r"({d})|(!?)\[(.+?)\]\((.+?)\)".format(
] ]
]] ]]
))) )))
URL_RE = re.compile(r"(!?)\[(.+?)\]\((.+?)\)")
OPENING_TAG = "<{}>" OPENING_TAG = "<{}>"
CLOSING_TAG = "</{}>" CLOSING_TAG = "</{}>"
@ -118,7 +119,7 @@ class Markdown:
for i, match in enumerate(re.finditer(MARKDOWN_RE, text)): for i, match in enumerate(re.finditer(MARKDOWN_RE, text)):
start, _ = match.span() start, _ = match.span()
delim, is_emoji, text_url, url = match.groups() delim = match.group(1)
full = match.group(0) full = match.group(0)
if delim in FIXED_WIDTH_DELIMS: if delim in FIXED_WIDTH_DELIMS:
@ -127,16 +128,6 @@ class Markdown:
if is_fixed_width and delim not in FIXED_WIDTH_DELIMS: if is_fixed_width and delim not in FIXED_WIDTH_DELIMS:
continue continue
if not is_emoji and text_url:
text = utils.replace_once(text, full, URL_MARKUP.format(url, text_url), start)
continue
if is_emoji:
emoji = text_url
emoji_id = url.lstrip("tg://emoji?id=")
text = utils.replace_once(text, full, EMOJI_MARKUP.format(emoji_id, emoji), start)
continue
if delim == BOLD_DELIM: if delim == BOLD_DELIM:
tag = "b" tag = "b"
elif delim == ITALIC_DELIM: elif delim == ITALIC_DELIM:
@ -169,6 +160,21 @@ class Markdown:
text = utils.replace_once(text, delim, tag, start) text = utils.replace_once(text, delim, tag, start)
for i, match in enumerate(re.finditer(URL_RE, text)):
start, _ = match.span()
is_emoji, text_url, url = match.groups()
full = match.group(0)
if not is_emoji and text_url:
text = utils.replace_once(text, full, URL_MARKUP.format(url, text_url), start)
continue
if is_emoji:
emoji = text_url
emoji_id = url.lstrip("tg://emoji?id=")
text = utils.replace_once(text, full, EMOJI_MARKUP.format(emoji_id, emoji), start)
continue
for placeholder, code_section in placeholders.items(): for placeholder, code_section in placeholders.items():
text = text.replace(placeholder, code_section) text = text.replace(placeholder, code_section)
@ -176,78 +182,110 @@ class Markdown:
@staticmethod @staticmethod
def unparse(text: str, entities: list): def unparse(text: str, entities: list):
"""
Performs the reverse operation to .parse(), effectively returning
markdown-like syntax given a normal text and its MessageEntity's.
:param text: the text to be reconverted into markdown.
:param entities: list of MessageEntity's applied to the text.
:return: a markdown-like text representing the combination of both inputs.
"""
delimiters = {
MessageEntityType.BOLD: BOLD_DELIM,
MessageEntityType.ITALIC: ITALIC_DELIM,
MessageEntityType.UNDERLINE: UNDERLINE_DELIM,
MessageEntityType.STRIKETHROUGH: STRIKE_DELIM,
MessageEntityType.CODE: CODE_DELIM,
MessageEntityType.PRE: PRE_DELIM,
MessageEntityType.BLOCKQUOTE: BLOCKQUOTE_DELIM,
MessageEntityType.EXPANDABLE_BLOCKQUOTE: BLOCKQUOTE_EXPANDABLE_DELIM,
MessageEntityType.SPOILER: SPOILER_DELIM
}
text = utils.add_surrogates(text) text = utils.add_surrogates(text)
entities_offsets = [] insert_at = []
for i, entity in enumerate(entities):
for entity in entities: s = entity.offset
entity_type = entity.type e = entity.offset + entity.length
start = entity.offset delimiter = delimiters.get(entity.type, None)
end = start + entity.length if delimiter:
if entity.type == MessageEntityType.PRE:
if entity_type == MessageEntityType.BOLD: inside_blockquote = any(
start_tag = end_tag = BOLD_DELIM blk_entity.offset <= s < blk_entity.offset + blk_entity.length and
elif entity_type == MessageEntityType.ITALIC: blk_entity.offset < e <= blk_entity.offset + blk_entity.length
start_tag = end_tag = ITALIC_DELIM for blk_entity in entities
elif entity_type == MessageEntityType.UNDERLINE: if blk_entity.type == MessageEntityType.BLOCKQUOTE
start_tag = end_tag = UNDERLINE_DELIM )
elif entity_type == MessageEntityType.STRIKETHROUGH: is_expandable = any(
start_tag = end_tag = STRIKE_DELIM blk_entity.offset <= s < blk_entity.offset + blk_entity.length and
elif entity_type == MessageEntityType.CODE: blk_entity.offset < e <= blk_entity.offset + blk_entity.length and
start_tag = end_tag = CODE_DELIM blk_entity.collapsed
elif entity_type == MessageEntityType.PRE: for blk_entity in entities
language = getattr(entity, "language", "") or "" if blk_entity.type == MessageEntityType.BLOCKQUOTE
start_tag = f"{PRE_DELIM}{language}\n" )
end_tag = f"\n{PRE_DELIM}" if inside_blockquote:
elif entity_type == MessageEntityType.BLOCKQUOTE: if is_expandable:
if entity.collapsed: if entity.language:
start_tag = BLOCKQUOTE_EXPANDABLE_DELIM + " " open_delimiter = f"{delimiter}{entity.language}\n**>"
else:
open_delimiter = f"{delimiter}\n**>"
close_delimiter = f"\n**>{delimiter}"
else:
if entity.language:
open_delimiter = f"{delimiter}{entity.language}\n>"
else:
open_delimiter = f"{delimiter}\n>"
close_delimiter = f"\n>{delimiter}"
else:
open_delimiter = delimiter
close_delimiter = delimiter
insert_at.append((s, i, open_delimiter))
insert_at.append((e, -i, close_delimiter))
elif entity.type != MessageEntityType.BLOCKQUOTE and entity.type != MessageEntityType.EXPANDABLE_BLOCKQUOTE:
open_delimiter = delimiter
close_delimiter = delimiter
insert_at.append((s, i, open_delimiter))
insert_at.append((e, -i, close_delimiter))
else: else:
start_tag = BLOCKQUOTE_DELIM + " " # Handle multiline blockquotes
end_tag = "" text_subset = text[s:e]
blockquote_text = text[start:end] lines = text_subset.splitlines()
lines = blockquote_text.split("\n") for line_num, line in enumerate(lines):
last_length = 0 line_start = s + sum(len(l) + 1 for l in lines[:line_num])
for line in lines: if entity.collapsed:
if len(line) == 0 and last_length == end: insert_at.append((line_start, i, BLOCKQUOTE_EXPANDABLE_DELIM))
continue else:
start_offset = start+last_length insert_at.append((line_start, i, BLOCKQUOTE_DELIM))
last_length = last_length+len(line) # No closing delimiter for blockquotes
end_offset = start_offset+last_length
entities_offsets.append((start_tag, start_offset,))
entities_offsets.append((end_tag, end_offset,))
last_length = last_length+1
continue
elif entity_type == MessageEntityType.SPOILER:
start_tag = end_tag = SPOILER_DELIM
elif entity_type == MessageEntityType.TEXT_LINK:
url = entity.url
start_tag = "["
end_tag = f"]({url})"
elif entity_type == MessageEntityType.TEXT_MENTION:
user = entity.user
start_tag = "["
end_tag = f"](tg://user?id={user.id})"
elif entity_type == MessageEntityType.CUSTOM_EMOJI:
emoji_id = entity.custom_emoji_id
start_tag = "!["
end_tag = f"](tg://emoji?id={emoji_id})"
else: else:
continue url = None
is_emoji = False
if entity.type == MessageEntityType.TEXT_LINK:
url = entity.url
elif entity.type == MessageEntityType.TEXT_MENTION:
url = f'tg://user?id={entity.user.id}'
elif entity.type == MessageEntityType.CUSTOM_EMOJI:
url = f"tg://emoji?id={entity.custom_emoji_id}"
is_emoji = True
if url:
if is_emoji:
insert_at.append((s, i, '!['))
else:
insert_at.append((s, i, '['))
insert_at.append((e, -i, f']({url})'))
entities_offsets.append((start_tag, start,)) insert_at.sort(key=lambda t: (t[0], t[1]))
entities_offsets.append((end_tag, end,)) while insert_at:
at, _, what = insert_at.pop()
entities_offsets = map( # If we are in the middle of a surrogate nudge the position by -1.
lambda x: x[1], # Otherwise we would end up with malformed text and fail to encode.
sorted( # For example of bad input: "Hi \ud83d\ude1c"
enumerate(entities_offsets), # https://en.wikipedia.org/wiki/UTF-16#U+010000_to_U+10FFFF
key=lambda x: (x[1][1], x[0]), while utils.within_surrogate(text, at):
reverse=True at += 1
)
)
for entity, offset in entities_offsets: text = text[:at] + what + text[at:]
text = text[:offset] + entity + text[offset:]
return utils.remove_surrogates(text) return utils.remove_surrogates(text)

View file

@ -40,3 +40,16 @@ def remove_surrogates(text):
def replace_once(source: str, old: str, new: str, start: int): def replace_once(source: str, old: str, new: str, start: int):
return source[:start] + source[start:].replace(old, new, 1) return source[:start] + source[start:].replace(old, new, 1)
def within_surrogate(text, index, *, length=None):
"""
`True` if ``index`` is within a surrogate (before and after it, not at!).
"""
if length is None:
length = len(text)
return (
1 < index < len(text) and # in bounds
'\ud800' <= text[index - 1] <= '\udbff' and # previous is
'\ud800' <= text[index] <= '\udfff' # current is
)

View file

@ -23,9 +23,7 @@ from functools import partial
from typing import List, Match, Union, BinaryIO, Optional, Callable from typing import List, Match, Union, BinaryIO, Optional, Callable
import pyrogram import pyrogram
from pyrogram import raw, enums from pyrogram import enums, raw, types, utils
from pyrogram import types
from pyrogram import utils
from pyrogram.errors import ChannelPrivate, MessageIdsEmpty, PeerIdInvalid from pyrogram.errors import ChannelPrivate, MessageIdsEmpty, PeerIdInvalid
from pyrogram.parser import utils as parser_utils, Parser from pyrogram.parser import utils as parser_utils, Parser
from ..object import Object from ..object import Object
@ -424,6 +422,9 @@ class Message(Object, Update):
link (``str``, *property*): link (``str``, *property*):
Generate a link to this message, only for groups and channels. Generate a link to this message, only for groups and channels.
content (``str``, *property*):
The text or caption content of the message.
scheduled (``bool``, *optional*): scheduled (``bool``, *optional*):
Message is a scheduled message and still in schedule. Message is a scheduled message and still in schedule.
@ -1112,7 +1113,7 @@ class Message(Object, Update):
video_note = types.VideoNote._parse(client, doc, video_attributes) video_note = types.VideoNote._parse(client, doc, video_attributes)
media_type = enums.MessageMediaType.VIDEO_NOTE media_type = enums.MessageMediaType.VIDEO_NOTE
else: else:
video = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds) video = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds, media.video_cover, media.video_timestamp)
media_type = enums.MessageMediaType.VIDEO media_type = enums.MessageMediaType.VIDEO
has_media_spoiler = media.spoiler has_media_spoiler = media.spoiler
@ -1358,6 +1359,10 @@ class Message(Object, Update):
else: else:
return f"https://t.me/c/{utils.get_channel_id(self.chat.id)}/{self.id}" return f"https://t.me/c/{utils.get_channel_id(self.chat.id)}/{self.id}"
@property
def content(self) -> str:
return self.text or self.caption or Str("").init([])
async def get_media_group(self) -> List["types.Message"]: async def get_media_group(self) -> List["types.Message"]:
"""Bound method *get_media_group* of :obj:`~pyrogram.types.Message`. """Bound method *get_media_group* of :obj:`~pyrogram.types.Message`.
@ -3593,7 +3598,7 @@ class Message(Object, Update):
quote_entities: List["types.MessageEntity"] = None, quote_entities: List["types.MessageEntity"] = None,
allow_paid_broadcast: bool = None, allow_paid_broadcast: bool = None,
message_effect_id: int = None, message_effect_id: int = None,
cover: Optional[Union[str, "io.BytesIO"]] = None, cover: Union[str, BinaryIO] = None,
start_timestamp: int = None, start_timestamp: int = None,
schedule_date: datetime = None, schedule_date: datetime = None,
invert_media: bool = None, invert_media: bool = None,
@ -3700,8 +3705,12 @@ class Message(Object, Update):
allow_paid_broadcast (``bool``, *optional*): allow_paid_broadcast (``bool``, *optional*):
Pass True to allow the message to ignore regular broadcast limits for a small fee; for bots. Pass True to allow the message to ignore regular broadcast limits for a small fee; for bots.
cover (``str`` | :obj:`io.BytesIO`, *optional*): cover (``str`` | ``BinaryIO``, *optional*):
Cover of the video; pass None to skip cover uploading. Video cover.
Pass a file_id as string to attach a photo that exists on the Telegram servers,
pass a HTTP URL as a string for Telegram to get a video from the Internet,
pass a file path as string to upload a new photo civer that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
start_timestamp (``int``, *optional*): start_timestamp (``int``, *optional*):
Timestamp from which the video playing must start, in seconds. Timestamp from which the video playing must start, in seconds.

View file

@ -21,8 +21,7 @@ from datetime import datetime
from typing import List from typing import List
import pyrogram import pyrogram
from pyrogram import raw, utils from pyrogram import raw, types, utils
from pyrogram import types
from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, ThumbnailSource from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, ThumbnailSource
from ..object import Object from ..object import Object
@ -67,6 +66,12 @@ class Video(Object):
thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*): thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
Video thumbnails. Video thumbnails.
cover (:obj:`~pyrogram.types.Photo`, *optional*):
Video cover.
start_timestamp (``int``, *optional*):
Video startpoint, in seconds.
""" """
def __init__( def __init__(
@ -84,7 +89,9 @@ class Video(Object):
supports_streaming: bool = None, supports_streaming: bool = None,
ttl_seconds: int = None, ttl_seconds: int = None,
date: datetime = None, date: datetime = None,
thumbs: List["types.Thumbnail"] = None thumbs: List["types.Thumbnail"] = None,
cover: "types.Photo" = None,
start_timestamp: int = None,
): ):
super().__init__(client) super().__init__(client)
@ -100,6 +107,8 @@ class Video(Object):
self.ttl_seconds = ttl_seconds self.ttl_seconds = ttl_seconds
self.date = date self.date = date
self.thumbs = thumbs self.thumbs = thumbs
self.cover = cover
self.start_timestamp = start_timestamp
@staticmethod @staticmethod
def _parse( def _parse(
@ -107,7 +116,9 @@ class Video(Object):
video: "raw.types.Document", video: "raw.types.Document",
video_attributes: "raw.types.DocumentAttributeVideo", video_attributes: "raw.types.DocumentAttributeVideo",
file_name: str, file_name: str,
ttl_seconds: int = None ttl_seconds: int = None,
cover = None,
start_timestamp: int = None
) -> "Video": ) -> "Video":
return Video( return Video(
file_id=FileId( file_id=FileId(
@ -131,5 +142,7 @@ class Video(Object):
date=utils.timestamp_to_datetime(video.date), date=utils.timestamp_to_datetime(video.date),
ttl_seconds=ttl_seconds, ttl_seconds=ttl_seconds,
thumbs=types.Thumbnail._parse(client, video), thumbs=types.Thumbnail._parse(client, video),
cover=types.Photo._parse(client, cover),
start_timestamp=start_timestamp,
client=client client=client
) )

View file

@ -50,6 +50,7 @@ from .username import Username
from .forum_topic import ForumTopic from .forum_topic import ForumTopic
from .forum_topic_created import ForumTopicCreated from .forum_topic_created import ForumTopicCreated
from .forum_topic_closed import ForumTopicClosed from .forum_topic_closed import ForumTopicClosed
from .forum_topic_deleted import ForumTopicDeleted
from .forum_topic_reopened import ForumTopicReopened from .forum_topic_reopened import ForumTopicReopened
from .forum_topic_edited import ForumTopicEdited from .forum_topic_edited import ForumTopicEdited
from .general_forum_topic_hidden import GeneralTopicHidden from .general_forum_topic_hidden import GeneralTopicHidden
@ -88,6 +89,7 @@ __all__ = [
"ForumTopic", "ForumTopic",
"ForumTopicCreated", "ForumTopicCreated",
"ForumTopicClosed", "ForumTopicClosed",
"ForumTopicDeleted",
"ForumTopicReopened", "ForumTopicReopened",
"ForumTopicEdited", "ForumTopicEdited",
"GeneralTopicHidden", "GeneralTopicHidden",

View file

@ -123,7 +123,9 @@ class ForumTopic(Object):
#self.draft = draft //todo #self.draft = draft //todo
@staticmethod @staticmethod
def _parse(forum_topic: "raw.types.forum_topic") -> "ForumTopic": def _parse(forum_topic: "raw.types.ForumTopic") -> "ForumTopic":
if isinstance(forum_topic, raw.types.ForumTopicDeleted):
return types.ForumTopicDeleted._parse(forum_topic)
from_id = forum_topic.from_id from_id = forum_topic.from_id
if isinstance(from_id, raw.types.PeerChannel): if isinstance(from_id, raw.types.PeerChannel):
peer = types.PeerChannel._parse(from_id) peer = types.PeerChannel._parse(from_id)

View file

@ -0,0 +1,45 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>
#
# This file is part of Pyrofork.
#
# Pyrofork 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.
#
# Pyrofork 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 Pyrofork. If not, see <http://www.gnu.org/licenses/>.
from pyrogram import raw, types
from typing import Union
from ..object import Object
class ForumTopicDeleted(Object):
"""A deleted forum topic.
Parameters:
id (``Integer``):
Id of the topic
"""
def __init__(
self,
*,
id: int
):
super().__init__()
self.id = id
@staticmethod
def _parse(forum_topic: "raw.types.ForumTopicDeleted") -> "ForumTopicDeleted":
return ForumTopicDeleted(
id=getattr(forum_topic,"id", None)
)