Compare commits

...

8 commits

Author SHA1 Message Date
dependabot[bot]
470f439bc3
Merge 0432034bed into 907f03197a 2025-06-10 05:39:58 +02:00
wulan17
907f03197a
pyrofork: Bump version to 2.3.64
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@komodos.id>
2025-06-06 19:10:08 +07:00
Ling-ex
eb2b854ea6
Update Gift class: add ton_address and clarify owner info.
Signed-off-by: Ling-ex <nekochan@rizkiofficial.com>
Signed-off-by: wulan17 <wulan17@komodos.id>
2025-06-06 19:08:43 +07:00
Ling-ex
7d10a6fb9c
Fix: Register RawUpdateHandler and Refactor Dispatcher for Better Modularity.
Signed-off-by: Ling-ex <nekochan@rizkiofficial.com>
Signed-off-by: wulan17 <wulan17@komodos.id>
2025-06-06 19:08:42 +07:00
wulan17
315f61d1ed
pyrofork: fix enums typo in Message (#135)
Signed-off-by: wulan17 <wulan17@komodos.id>
2025-06-06 19:08:42 +07:00
wulan17
f6599d8ef4
pyrofork: Add missing Username docs
Signed-off-by: wulan17 <wulan17@komodos.id>
2025-06-06 19:08:41 +07:00
wulan17
47b054c996
pyrofork: filters: Add support for fragments usernames
Signed-off-by: wulan17 <wulan17@komodos.id>
2025-06-06 19:08:40 +07:00
dependabot[bot]
0432034bed
build(deps): bump sphinx-immaterial from 0.12.4 to 0.13.4
Bumps [sphinx-immaterial](https://github.com/jbms/sphinx-immaterial) from 0.12.4 to 0.13.4.
- [Release notes](https://github.com/jbms/sphinx-immaterial/releases)
- [Commits](https://github.com/jbms/sphinx-immaterial/compare/v0.12.4...v0.13.4)

---
updated-dependencies:
- dependency-name: sphinx-immaterial
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-19 22:08:48 +00:00
7 changed files with 149 additions and 61 deletions

View file

@ -484,6 +484,7 @@ def pyrogram_api():
BusinessWeeklyOpen BusinessWeeklyOpen
BusinessWorkingHours BusinessWorkingHours
User User
Username
Chat Chat
ChatPreview ChatPreview
ChatPhoto ChatPhoto

View file

@ -60,7 +60,7 @@ dev = [
docs = [ docs = [
"sphinx", "sphinx",
"sphinx-immaterial==0.12.4", "sphinx-immaterial==0.13.4",
"sphinx_copybutton", "sphinx_copybutton",
"sphinx-autobuild", "sphinx-autobuild",
"tornado>=6.3.3" "tornado>=6.3.3"

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.63" __version__ = "2.3.64"
__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

@ -21,6 +21,7 @@ import asyncio
import inspect import inspect
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from typing import Any
import pyrogram import pyrogram
from pyrogram import raw, types, utils from pyrogram import raw, types, utils
@ -337,59 +338,88 @@ class Dispatcher:
async def handler_worker(self, lock: asyncio.Lock): async def handler_worker(self, lock: asyncio.Lock):
while True: while True:
packet = await self.updates_queue.get() packet = await self.updates_queue.get()
if packet is None: if packet is None:
break break
await self._process_packet(packet, lock)
async def _process_packet( try:
self, await self._handle_packet(packet, lock)
packet: tuple[raw.core.TLObject, dict[int, types.Update], dict[int, types.Update]], except pyrogram.StopPropagation:
lock: asyncio.Lock, pass
except Exception as e:
log.exception(e)
finally:
self.updates_queue.task_done()
async def _handle_packet(self, packet, lock: asyncio.Lock):
update, users, chats = packet
parser = self.update_parsers.get(type(update))
parsed_update, handler_type = (
await parser(update, users, chats)
if parser is not None else (None, type(None))
)
async with lock:
await self._dispatch_to_handlers(update, users, chats, parsed_update, handler_type)
async def _dispatch_to_handlers(
self, update, users, chats, parsed_update, handler_type,
):
for group in self.groups.values():
for handler in group:
args = await self._match_handler(
handler, update, users, chats, parsed_update, handler_type,
)
if args is None:
continue
try:
await self._execute_handler(handler, *args)
except pyrogram.StopPropagation:
raise
except pyrogram.ContinuePropagation:
continue
except Exception as error:
if parsed_update is not None:
await self._handle_exception(parsed_update, error)
break
async def _match_handler(
self, handler, update, users, chats, parsed_update, handler_type,
): ):
try: try:
update, users, chats = packet if isinstance(handler, handler_type):
parser = self.update_parsers.get(type(update)) if await handler.check(self.client, parsed_update):
return (parsed_update,)
if parser is not None: elif isinstance(handler, RawUpdateHandler):
parsed_result = parser(update, users, chats) if await handler.check(self.client, update):
if inspect.isawaitable(parsed_result): return (update, users, chats)
parsed_update, handler_type = await parsed_result
else:
parsed_update, handler_type = parsed_result
else:
parsed_update, handler_type = (None, type(None))
async with lock:
for group in self.groups.values():
for handler in group:
try:
if parsed_update is not None:
if isinstance(handler, handler_type) and await handler.check(
self.client, parsed_update
):
await self._execute_callback(handler, parsed_update)
break
elif isinstance(handler, RawUpdateHandler):
await self._execute_callback(handler, update, users, chats)
break
except (pyrogram.StopPropagation, pyrogram.ContinuePropagation) as e:
if isinstance(e, pyrogram.StopPropagation):
raise
except Exception as exception:
if parsed_update is not None:
await self._handle_exception(parsed_update, exception)
except pyrogram.StopPropagation:
pass
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
finally:
self.updates_queue.task_done()
async def _handle_exception(self, parsed_update: types.Update, exception: Exception): return None
async def _execute_handler(self, handler, *args: Any):
if inspect.iscoroutinefunction(handler.callback):
await handler.callback(self.client, *args)
else:
await self.loop.run_in_executor(
self.client.executor,
handler.callback,
self.client,
*args
)
async def _handle_exception(
self, parsed_update: types.Update, exception: Exception,
):
handled_error = False handled_error = False
for error_handler in self.error_handlers: for error_handler in self.error_handlers:
try: try:
if await error_handler.check(self.client, parsed_update, exception): if await error_handler.check(
self.client, parsed_update, exception,
):
handled_error = True handled_error = True
break break
except pyrogram.StopPropagation: except pyrogram.StopPropagation:
@ -401,11 +431,3 @@ class Dispatcher:
if not handled_error: if not handled_error:
log.exception("Unhandled exception: %s", exception) log.exception("Unhandled exception: %s", exception)
async def _execute_callback(self, handler: Handler, *args):
if inspect.iscoroutinefunction(handler.callback):
await handler.callback(self.client, *args)
else:
await self.client.loop.run_in_executor(
self.client.executor, handler.callback, self.client, *args
)

View file

@ -890,7 +890,12 @@ def command(commands: Union[str, List[str]], prefixes: Union[str, List[str]] = "
command_re = re.compile(r"([\"'])(.*?)(?<!\\)\1|(\S+)") command_re = re.compile(r"([\"'])(.*?)(?<!\\)\1|(\S+)")
async def func(flt, client: pyrogram.Client, message: Message): async def func(flt, client: pyrogram.Client, message: Message):
usernames = []
username = client.me.username or "" username = client.me.username or ""
if client.me.usernames:
usernames.append(username)
for user in client.me.usernames:
usernames.append(user.username)
text = message.text or message.caption text = message.text or message.caption
message.command = None message.command = None
@ -904,6 +909,24 @@ def command(commands: Union[str, List[str]], prefixes: Union[str, List[str]] = "
without_prefix = text[len(prefix):] without_prefix = text[len(prefix):]
for cmd in flt.commands: for cmd in flt.commands:
if usernames:
for username in usernames:
if not re.match(rf"^(?:{cmd}(?:@?{username})?)(?:\s|$)", without_prefix,
flags=re.IGNORECASE if not flt.case_sensitive else 0):
continue
without_command = re.sub(rf"{cmd}(?:@?{username})?\s?", "", without_prefix, count=1,
flags=re.IGNORECASE if not flt.case_sensitive else 0)
# match.groups are 1-indexed, group(1) is the quote, group(2) is the text
# between the quotes, group(3) is unquoted, whitespace-split text
# Remove the escape character from the arguments
message.command = [cmd] + [
re.sub(r"\\([\"'])", r"\1", m.group(2) or m.group(3) or "")
for m in command_re.finditer(without_command)
]
return True
if not re.match(rf"^(?:{cmd}(?:@?{username})?)(?:\s|$)", without_prefix, if not re.match(rf"^(?:{cmd}(?:@?{username})?)(?:\s|$)", without_prefix,
flags=re.IGNORECASE if not flt.case_sensitive else 0): flags=re.IGNORECASE if not flt.case_sensitive else 0):
continue continue
@ -1011,12 +1034,22 @@ class user(Filter, set):
) )
async def __call__(self, _, message: Message): async def __call__(self, _, message: Message):
is_usernames_in_filters = False
if message.from_user.usernames:
for username in message.from_user.usernames:
if (
username.username in self
or username.username.lower() in self
):
is_usernames_in_filters = True
break
return (message.from_user return (message.from_user
and (message.from_user.id in self and (message.from_user.id in self
or (message.from_user.username or (message.from_user.username
and message.from_user.username.lower() in self) and message.from_user.username.lower() in self)
or ("me" in self or ("me" in self
and message.from_user.is_self))) and message.from_user.is_self))
or is_usernames_in_filters)
# noinspection PyPep8Naming # noinspection PyPep8Naming
@ -1044,6 +1077,15 @@ class chat(Filter, set):
async def __call__(self, _, message: Union[Message, Story]): async def __call__(self, _, message: Union[Message, Story]):
if isinstance(message, Story): if isinstance(message, Story):
is_usernames_in_filters = False
if message.sender_chat.usernames:
for username in message.sender_chat.usernames:
if (
username.username in self
or username.username.lower() in self
):
is_usernames_in_filters = True
break
return ( return (
message.sender_chat message.sender_chat
and ( and (
@ -1062,8 +1104,17 @@ class chat(Filter, set):
and message.from_user.username.lower() in self and message.from_user.username.lower() in self
) )
) )
) ) or is_usernames_in_filters
else: else:
is_usernames_in_filters = False
if message.chat.usernames:
for username in message._chat.usernames:
if (
username.username in self
or username.username.lower() in self
):
is_usernames_in_filters = True
break
return (message.chat return (message.chat
and (message.chat.id in self and (message.chat.id in self
or (message.chat.username or (message.chat.username
@ -1071,7 +1122,10 @@ class chat(Filter, set):
or ("me" in self or ("me" in self
and message.from_user and message.from_user
and message.from_user.is_self and message.from_user.is_self
and not message.outgoing))) and not message.outgoing))
or (is_usernames_in_filters
and not message.outgoing)
)
# noinspection PyPep8Naming # noinspection PyPep8Naming

View file

@ -808,7 +808,7 @@ class Message(Object, Update):
service_type = enums.MessageServiceType.BOT_ALLOWED service_type = enums.MessageServiceType.BOT_ALLOWED
elif isinstance(action, raw.types.MessageActionRequestedPeer) or isinstance(action, raw.types.MessageActionRequestedPeerSentMe): elif isinstance(action, raw.types.MessageActionRequestedPeer) or isinstance(action, raw.types.MessageActionRequestedPeerSentMe):
chats_shared = await types.RequestedChats._parse(client, action) chats_shared = await types.RequestedChats._parse(client, action)
service_type = enums.MessageServiceType.ChatShared service_type = enums.MessageServiceType.CHAT_SHARED
elif isinstance(action, raw.types.MessageActionTopicCreate): elif isinstance(action, raw.types.MessageActionTopicCreate):
forum_topic_created = types.ForumTopicCreated._parse(message) forum_topic_created = types.ForumTopicCreated._parse(message)
service_type = enums.MessageServiceType.FORUM_TOPIC_CREATED service_type = enums.MessageServiceType.FORUM_TOPIC_CREATED

View file

@ -77,13 +77,16 @@ class Gift(Object):
User who sent the star gift. User who sent the star gift.
owner (:obj:`~pyrogram.types.Chat`, *optional*): owner (:obj:`~pyrogram.types.Chat`, *optional*):
Current gift owner. Only available if the nfts is in telegram.
owner_name (``str``, *optional*): owner_name (``str``, *optional*):
Name of the user who received the star gift. Name of the user who received the star gift.
owner_address (``str``, *optional*): owner_address (``str``, *optional*):
Address of the gift owner in TON blockchain. Only available if the nfts is in ton network.
ton_address (``str``, *optional*):
Only available if the nfts is in ton network.
price (``int``, *optional*): price (``int``, *optional*):
Price of this gift in stars. Price of this gift in stars.
@ -141,7 +144,7 @@ class Gift(Object):
raw (:obj:`~pyrogram.raw.base.StarGift`, *optional*): raw (:obj:`~pyrogram.raw.base.StarGift`, *optional*):
The raw object as received from the server. The raw object as received from the server.
link (``str``, *property*): link (``str``, *property*):
A link to the gift. A link to the gift.
For unique gifts only. For unique gifts only.
@ -163,6 +166,7 @@ class Gift(Object):
owner: Optional["types.Chat"] = None, owner: Optional["types.Chat"] = None,
owner_name: Optional[str] = None, owner_name: Optional[str] = None,
owner_address: Optional[str] = None, owner_address: Optional[str] = None,
ton_address: Optional[str] = None,
price: Optional[int] = None, price: Optional[int] = None,
convert_price: Optional[int] = None, convert_price: Optional[int] = None,
upgrade_price: Optional[int] = None, upgrade_price: Optional[int] = None,
@ -201,6 +205,7 @@ class Gift(Object):
self.owner = owner self.owner = owner
self.owner_name = owner_name self.owner_name = owner_name
self.owner_address = owner_address self.owner_address = owner_address
self.ton_address = ton_address
self.price = price self.price = price
self.convert_price = convert_price self.convert_price = convert_price
self.upgrade_price = upgrade_price self.upgrade_price = upgrade_price
@ -233,7 +238,7 @@ class Gift(Object):
return await Gift._parse_unique(client, gift, users, chats) return await Gift._parse_unique(client, gift, users, chats)
elif isinstance(gift, raw.types.StarGiftSaved): elif isinstance(gift, raw.types.StarGiftSaved):
return await Gift._parse_saved(client, gift, users, chats) return await Gift._parse_saved(client, gift, users, chats)
@staticmethod @staticmethod
async def _parse_regular( async def _parse_regular(
client, client,
@ -277,9 +282,15 @@ class Gift(Object):
) or None, ) or None,
available_amount=getattr(star_gift, "availability_issued", None), available_amount=getattr(star_gift, "availability_issued", None),
total_amount=getattr(star_gift, "availability_total", None), total_amount=getattr(star_gift, "availability_total", None),
owner=types.Chat._parse_chat(client, users.get(owner_id) or chats.get(owner_id)), owner=(
types.Chat._parse_chat(client, users.get(owner_id) or
chats.get(owner_id))
if owner_id is not None
else None
),
owner_name=getattr(star_gift, "owner_name", None), owner_name=getattr(star_gift, "owner_name", None),
owner_address=getattr(star_gift, "owner_address", None), owner_address=getattr(star_gift, "owner_address", None),
ton_address=getattr(star_gift, "gift_address", None),
is_upgraded=True, is_upgraded=True,
raw=star_gift, raw=star_gift,
client=client client=client