mirror of
https://github.com/pyrogram/pyrogram
synced 2025-09-01 23:05:15 +00:00
Merge branch 'develop' into asyncio
# Conflicts: # pyrogram/client/client.py
This commit is contained in:
@@ -159,10 +159,8 @@ class Client(Methods, BaseClient):
|
||||
config_file (``str``, *optional*):
|
||||
Path of the configuration file. Defaults to ./config.ini
|
||||
|
||||
plugins_dir (``str``, *optional*):
|
||||
Define a custom directory for your plugins. The plugins directory is the location in your
|
||||
filesystem where Pyrogram will automatically load your update handlers.
|
||||
Defaults to None (plugins disabled).
|
||||
plugins (``dict``, *optional*):
|
||||
TODO: doctrings
|
||||
|
||||
no_updates (``bool``, *optional*):
|
||||
Pass True to completely disable incoming updates for the current session.
|
||||
@@ -199,7 +197,7 @@ class Client(Methods, BaseClient):
|
||||
workers: int = BaseClient.WORKERS,
|
||||
workdir: str = BaseClient.WORKDIR,
|
||||
config_file: str = BaseClient.CONFIG_FILE,
|
||||
plugins_dir: str = None,
|
||||
plugins: dict = None,
|
||||
no_updates: bool = None,
|
||||
takeout: bool = None):
|
||||
super().__init__()
|
||||
@@ -225,7 +223,7 @@ class Client(Methods, BaseClient):
|
||||
self.workers = workers
|
||||
self.workdir = workdir
|
||||
self.config_file = config_file
|
||||
self.plugins_dir = plugins_dir
|
||||
self.plugins = plugins
|
||||
self.no_updates = no_updates
|
||||
self.takeout = takeout
|
||||
|
||||
@@ -243,7 +241,14 @@ class Client(Methods, BaseClient):
|
||||
|
||||
@proxy.setter
|
||||
def proxy(self, value):
|
||||
self._proxy["enabled"] = True
|
||||
if value is None:
|
||||
self._proxy = None
|
||||
return
|
||||
|
||||
if self._proxy is None:
|
||||
self._proxy = {}
|
||||
|
||||
self._proxy["enabled"] = bool(value.get("enabled", True))
|
||||
self._proxy.update(value)
|
||||
|
||||
async def start(self):
|
||||
@@ -1056,17 +1061,41 @@ class Client(Methods, BaseClient):
|
||||
setattr(self, option, getattr(Client, option.upper()))
|
||||
|
||||
if self._proxy:
|
||||
self._proxy["enabled"] = True
|
||||
self._proxy["enabled"] = bool(self._proxy.get("enabled", True))
|
||||
else:
|
||||
self._proxy = {}
|
||||
|
||||
if parser.has_section("proxy"):
|
||||
self._proxy["enabled"] = parser.getboolean("proxy", "enabled")
|
||||
self._proxy["enabled"] = parser.getboolean("proxy", "enabled", fallback=True)
|
||||
self._proxy["hostname"] = parser.get("proxy", "hostname")
|
||||
self._proxy["port"] = parser.getint("proxy", "port")
|
||||
self._proxy["username"] = parser.get("proxy", "username", fallback=None) or None
|
||||
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
|
||||
|
||||
if self.plugins:
|
||||
self.plugins["enabled"] = bool(self.plugins.get("enabled", True))
|
||||
self.plugins["include"] = "\n".join(self.plugins.get("include", [])) or None
|
||||
self.plugins["exclude"] = "\n".join(self.plugins.get("exclude", [])) or None
|
||||
else:
|
||||
try:
|
||||
section = parser["plugins"]
|
||||
|
||||
self.plugins = {
|
||||
"enabled": section.getboolean("enabled", True),
|
||||
"root": section.get("root"),
|
||||
"include": section.get("include") or None,
|
||||
"exclude": section.get("exclude") or None
|
||||
}
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for option in ["include", "exclude"]:
|
||||
if self.plugins[option] is not None:
|
||||
self.plugins[option] = [
|
||||
(i.split()[0], i.split()[1:] or None)
|
||||
for i in self.plugins[option].strip().split("\n")
|
||||
]
|
||||
|
||||
async def load_session(self):
|
||||
try:
|
||||
with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), encoding="utf-8") as f:
|
||||
@@ -1098,43 +1127,108 @@ class Client(Methods, BaseClient):
|
||||
self.peers_by_phone[k] = peer
|
||||
|
||||
def load_plugins(self):
|
||||
if self.plugins_dir is not None:
|
||||
plugins_count = 0
|
||||
if self.plugins.get("enabled", False):
|
||||
root = self.plugins["root"]
|
||||
include = self.plugins["include"]
|
||||
exclude = self.plugins["exclude"]
|
||||
|
||||
for path in Path(self.plugins_dir).rglob("*.py"):
|
||||
file_path = os.path.splitext(str(path))[0]
|
||||
import_path = []
|
||||
count = 0
|
||||
|
||||
while file_path:
|
||||
file_path, tail = os.path.split(file_path)
|
||||
import_path.insert(0, tail)
|
||||
if include is None:
|
||||
for path in sorted(Path(root).rglob("*.py")):
|
||||
module_path = os.path.splitext(str(path))[0].replace("/", ".")
|
||||
module = import_module(module_path)
|
||||
|
||||
import_path = ".".join(import_path)
|
||||
module = import_module(import_path)
|
||||
for name in vars(module).keys():
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
handler, group = getattr(module, name)
|
||||
|
||||
for name in dir(module):
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
handler, group = getattr(module, name)
|
||||
if isinstance(handler, Handler) and isinstance(group, int):
|
||||
self.add_handler(handler, group)
|
||||
|
||||
if isinstance(handler, Handler) and isinstance(group, int):
|
||||
self.add_handler(handler, group)
|
||||
log.info('[LOAD] {}("{}") in group {} from "{}"'.format(
|
||||
type(handler).__name__, name, group, module_path))
|
||||
|
||||
log.info('{}("{}") from "{}" loaded in group {}'.format(
|
||||
type(handler).__name__, name, import_path, group))
|
||||
|
||||
plugins_count += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if plugins_count > 0:
|
||||
log.warning('Successfully loaded {} plugin{} from "{}"'.format(
|
||||
plugins_count,
|
||||
"s" if plugins_count > 1 else "",
|
||||
self.plugins_dir
|
||||
))
|
||||
count += 1
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
log.warning('No plugin loaded: "{}" doesn\'t contain any valid plugin'.format(self.plugins_dir))
|
||||
for path, handlers in include:
|
||||
module_path = root + "." + path
|
||||
warn_non_existent_functions = True
|
||||
|
||||
try:
|
||||
module = import_module(module_path)
|
||||
except ModuleNotFoundError:
|
||||
log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path))
|
||||
continue
|
||||
|
||||
if "__path__" in dir(module):
|
||||
log.warning('[LOAD] Ignoring namespace "{}"'.format(module_path))
|
||||
continue
|
||||
|
||||
if handlers is None:
|
||||
handlers = vars(module).keys()
|
||||
warn_non_existent_functions = False
|
||||
|
||||
for name in handlers:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
handler, group = getattr(module, name)
|
||||
|
||||
if isinstance(handler, Handler) and isinstance(group, int):
|
||||
self.add_handler(handler, group)
|
||||
|
||||
log.info('[LOAD] {}("{}") in group {} from "{}"'.format(
|
||||
type(handler).__name__, name, group, module_path))
|
||||
|
||||
count += 1
|
||||
except Exception:
|
||||
if warn_non_existent_functions:
|
||||
log.warning('[LOAD] Ignoring non-existent function "{}" from "{}"'.format(
|
||||
name, module_path))
|
||||
|
||||
if exclude is not None:
|
||||
for path, handlers in exclude:
|
||||
module_path = root + "." + path
|
||||
warn_non_existent_functions = True
|
||||
|
||||
try:
|
||||
module = import_module(module_path)
|
||||
except ModuleNotFoundError:
|
||||
log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path))
|
||||
continue
|
||||
|
||||
if "__path__" in dir(module):
|
||||
log.warning('[UNLOAD] Ignoring namespace "{}"'.format(module_path))
|
||||
continue
|
||||
|
||||
if handlers is None:
|
||||
handlers = vars(module).keys()
|
||||
warn_non_existent_functions = False
|
||||
|
||||
for name in handlers:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
handler, group = getattr(module, name)
|
||||
|
||||
if isinstance(handler, Handler) and isinstance(group, int):
|
||||
self.remove_handler(handler, group)
|
||||
|
||||
log.info('[UNLOAD] {}("{}") from group {} in "{}"'.format(
|
||||
type(handler).__name__, name, group, module_path))
|
||||
|
||||
count -= 1
|
||||
except Exception:
|
||||
if warn_non_existent_functions:
|
||||
log.warning('[UNLOAD] Ignoring non-existent function "{}" from "{}"'.format(
|
||||
name, module_path))
|
||||
|
||||
if count > 0:
|
||||
log.warning('Successfully loaded {} plugin{} from "{}"'.format(count, "s" if count > 1 else "", root))
|
||||
else:
|
||||
log.warning('No plugin loaded from "{}"'.format(root))
|
||||
|
||||
def save_session(self):
|
||||
auth_key = base64.b64encode(self.auth_key).decode()
|
||||
|
@@ -125,3 +125,6 @@ class BaseClient:
|
||||
|
||||
async def get_chat_members(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def get_chat_members_count(self, *args, **kwargs):
|
||||
pass
|
||||
|
@@ -67,6 +67,8 @@ class GetChatMember(BaseClient):
|
||||
)
|
||||
)
|
||||
|
||||
return pyrogram.ChatMember._parse(self, r.participant, r.users[0])
|
||||
users = {i.id: i for i in r.users}
|
||||
|
||||
return pyrogram.ChatMember._parse(self, r.participant, users)
|
||||
else:
|
||||
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
|
||||
|
@@ -84,9 +84,14 @@ class IterChatMembers(BaseClient):
|
||||
yielded = set()
|
||||
queries = [query] if query else QUERIES
|
||||
total = limit or (1 << 31) - 1
|
||||
filter = Filters.RECENT if total <= 10000 and filter == Filters.ALL else filter
|
||||
limit = min(200, total)
|
||||
|
||||
filter = (
|
||||
Filters.RECENT
|
||||
if self.get_chat_members_count(chat_id) <= 10000 and filter == Filters.ALL
|
||||
else filter
|
||||
)
|
||||
|
||||
if filter not in QUERYABLE_FILTERS:
|
||||
queries = [""]
|
||||
|
||||
|
@@ -33,6 +33,19 @@ class ChatMember(PyrogramType):
|
||||
The member's status in the chat. Can be "creator", "administrator", "member", "restricted",
|
||||
"left" or "kicked".
|
||||
|
||||
date (``int``, *optional*):
|
||||
Date when the user joined, unix time. Not available for creator.
|
||||
|
||||
invited_by (:obj:`User <pyrogram.User>`, *optional*):
|
||||
Administrators and self member only. Information about the user who invited this member.
|
||||
In case the user joined by himself this will be the same as "user".
|
||||
|
||||
promoted_by (:obj:`User <pyrogram.User>`, *optional*):
|
||||
Administrators only. Information about the user who promoted this member as administrator.
|
||||
|
||||
restricted_by (:obj:`User <pyrogram.User>`, *optional*):
|
||||
Restricted and kicked only. Information about the user who restricted or kicked this member.
|
||||
|
||||
until_date (``int``, *optional*):
|
||||
Restricted and kicked only. Date when restrictions will be lifted for this user, unix time.
|
||||
|
||||
@@ -86,6 +99,10 @@ class ChatMember(PyrogramType):
|
||||
client: "pyrogram.client.ext.BaseClient",
|
||||
user: "pyrogram.User",
|
||||
status: str,
|
||||
date: int = None,
|
||||
invited_by: "pyrogram.User" = None,
|
||||
promoted_by: "pyrogram.User" = None,
|
||||
restricted_by: "pyrogram.User" = None,
|
||||
until_date: int = None,
|
||||
can_be_edited: bool = None,
|
||||
can_change_info: bool = None,
|
||||
@@ -104,6 +121,10 @@ class ChatMember(PyrogramType):
|
||||
|
||||
self.user = user
|
||||
self.status = status
|
||||
self.date = date
|
||||
self.invited_by = invited_by
|
||||
self.promoted_by = promoted_by
|
||||
self.restricted_by = restricted_by
|
||||
self.until_date = until_date
|
||||
self.can_be_edited = can_be_edited
|
||||
self.can_change_info = can_change_info
|
||||
@@ -120,17 +141,18 @@ class ChatMember(PyrogramType):
|
||||
self.can_add_web_page_previews = can_add_web_page_previews
|
||||
|
||||
@staticmethod
|
||||
def _parse(client, member, user) -> "ChatMember":
|
||||
user = pyrogram.User._parse(client, user)
|
||||
def _parse(client, member, users) -> "ChatMember":
|
||||
user = pyrogram.User._parse(client, users[member.user_id])
|
||||
invited_by = pyrogram.User._parse(client, users[member.inviter_id]) if hasattr(member, "inviter_id") else None
|
||||
|
||||
if isinstance(member, (types.ChannelParticipant, types.ChannelParticipantSelf, types.ChatParticipant)):
|
||||
return ChatMember(user=user, status="member", client=client)
|
||||
return ChatMember(user=user, status="member", date=member.date, invited_by=invited_by, client=client)
|
||||
|
||||
if isinstance(member, (types.ChannelParticipantCreator, types.ChatParticipantCreator)):
|
||||
return ChatMember(user=user, status="creator", client=client)
|
||||
|
||||
if isinstance(member, types.ChatParticipantAdmin):
|
||||
return ChatMember(user=user, status="administrator", client=client)
|
||||
return ChatMember(user=user, status="administrator", date=member.date, invited_by=invited_by, client=client)
|
||||
|
||||
if isinstance(member, types.ChannelParticipantAdmin):
|
||||
rights = member.admin_rights
|
||||
@@ -138,6 +160,9 @@ class ChatMember(PyrogramType):
|
||||
return ChatMember(
|
||||
user=user,
|
||||
status="administrator",
|
||||
date=member.date,
|
||||
invited_by=invited_by,
|
||||
promoted_by=pyrogram.User._parse(client, users[member.promoted_by]),
|
||||
can_be_edited=member.can_edit,
|
||||
can_change_info=rights.change_info,
|
||||
can_post_messages=rights.post_messages,
|
||||
@@ -155,7 +180,13 @@ class ChatMember(PyrogramType):
|
||||
|
||||
chat_member = ChatMember(
|
||||
user=user,
|
||||
status="kicked" if rights.view_messages else "restricted",
|
||||
status=(
|
||||
"kicked" if rights.view_messages
|
||||
else "left" if member.left
|
||||
else "restricted"
|
||||
),
|
||||
date=member.date,
|
||||
restricted_by=pyrogram.User._parse(client, users[member.kicked_by]),
|
||||
until_date=0 if rights.until_date == (1 << 31) - 1 else rights.until_date,
|
||||
client=client
|
||||
)
|
||||
|
@@ -59,7 +59,7 @@ class ChatMembers(PyrogramType):
|
||||
total_count = len(members)
|
||||
|
||||
for member in members:
|
||||
chat_members.append(ChatMember._parse(client, member, users[member.user_id]))
|
||||
chat_members.append(ChatMember._parse(client, member, users))
|
||||
|
||||
return ChatMembers(
|
||||
total_count=total_count,
|
||||
|
Reference in New Issue
Block a user