mirror of
https://github.com/TeamPGM/PagerMaid-Pyro.git
synced 2024-11-24 19:07:37 +00:00
🔖 Update to v1.2.25
Support qr login.
This commit is contained in:
parent
efea7afd01
commit
aa42ed7372
1
.gitignore
vendored
1
.gitignore
vendored
@ -131,6 +131,7 @@ unknown_errors.txt
|
|||||||
pagermaid*.txt
|
pagermaid*.txt
|
||||||
exception*.txt
|
exception*.txt
|
||||||
output.log
|
output.log
|
||||||
|
qrcode.png
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
# API Credentials of your telegram application created at https://my.telegram.org/apps
|
# API Credentials of your telegram application created at https://my.telegram.org/apps
|
||||||
api_id: "ID_HERE"
|
api_id: "ID_HERE"
|
||||||
api_hash: "HASH_HERE"
|
api_hash: "HASH_HERE"
|
||||||
|
qrcode_login: "False"
|
||||||
|
|
||||||
# Either debug logging is enabled or not
|
# Either debug logging is enabled or not
|
||||||
debug: "False"
|
debug: "False"
|
||||||
|
@ -12,7 +12,7 @@ from pagermaid.scheduler import scheduler
|
|||||||
import pyromod.listen
|
import pyromod.listen
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
|
|
||||||
pgm_version = "1.2.24"
|
pgm_version = "1.2.25"
|
||||||
CMD_LIST = {}
|
CMD_LIST = {}
|
||||||
module_dir = __path__[0]
|
module_dir = __path__[0]
|
||||||
working_dir = getcwd()
|
working_dir = getcwd()
|
||||||
|
@ -10,6 +10,7 @@ from pagermaid.hook import Hook
|
|||||||
from pagermaid.modules import module_list, plugin_list
|
from pagermaid.modules import module_list, plugin_list
|
||||||
from pagermaid.single_utils import safe_remove
|
from pagermaid.single_utils import safe_remove
|
||||||
from pagermaid.utils import lang, process_exit
|
from pagermaid.utils import lang, process_exit
|
||||||
|
from pyromod.methods.sign_in_qrcode import start_client
|
||||||
|
|
||||||
path.insert(1, f"{working_dir}{sep}plugins")
|
path.insert(1, f"{working_dir}{sep}plugins")
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ async def main():
|
|||||||
logs.info(lang('platform') + platform + lang('platform_load'))
|
logs.info(lang('platform') + platform + lang('platform_load'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await bot.start()
|
await start_client(bot)
|
||||||
except AuthKeyUnregistered:
|
except AuthKeyUnregistered:
|
||||||
safe_remove("pagermaid.session")
|
safe_remove("pagermaid.session")
|
||||||
exit()
|
exit()
|
||||||
|
@ -43,6 +43,7 @@ class Config:
|
|||||||
# TGX
|
# TGX
|
||||||
API_ID = DEFAULT_API_ID
|
API_ID = DEFAULT_API_ID
|
||||||
API_HASH = DEFAULT_API_HASH
|
API_HASH = DEFAULT_API_HASH
|
||||||
|
QRCODE_LOGIN = strtobool(os.environ.get("QRCODE_LOGIN", config.get("qrcode_login", "false")))
|
||||||
STRING_SESSION = os.environ.get("STRING_SESSION")
|
STRING_SESSION = os.environ.get("STRING_SESSION")
|
||||||
DEBUG = strtobool(os.environ.get("PGM_DEBUG", config["debug"]))
|
DEBUG = strtobool(os.environ.get("PGM_DEBUG", config["debug"]))
|
||||||
ERROR_REPORT = strtobool(os.environ.get("PGM_ERROR_REPORT", config["error_report"]), True)
|
ERROR_REPORT = strtobool(os.environ.get("PGM_ERROR_REPORT", config["error_report"]), True)
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
"""
|
|
||||||
pyromod - A monkeypatcher add-on for Pyrogram
|
|
||||||
Copyright (C) 2020 Cezar H. <https://github.com/usernein>
|
|
||||||
|
|
||||||
This file is part of pyromod.
|
|
||||||
|
|
||||||
pyromod is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
pyromod 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 General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
from .helpers import ikb, bki, ntb, btn, kb, kbtn, array_chunk, force_reply
|
|
@ -1,75 +0,0 @@
|
|||||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ForceReply
|
|
||||||
|
|
||||||
|
|
||||||
def ikb(rows=None):
|
|
||||||
if rows is None:
|
|
||||||
rows = []
|
|
||||||
lines = []
|
|
||||||
for row in rows:
|
|
||||||
line = []
|
|
||||||
for button in row:
|
|
||||||
button = btn(*button) # InlineKeyboardButton
|
|
||||||
line.append(button)
|
|
||||||
lines.append(line)
|
|
||||||
return InlineKeyboardMarkup(inline_keyboard=lines)
|
|
||||||
# return {'inline_keyboard': lines}
|
|
||||||
|
|
||||||
|
|
||||||
def btn(text, value, type='callback_data'):
|
|
||||||
return InlineKeyboardButton(text, **{type: value})
|
|
||||||
# return {'text': text, type: value}
|
|
||||||
|
|
||||||
|
|
||||||
# The inverse of above
|
|
||||||
def bki(keyboard):
|
|
||||||
lines = []
|
|
||||||
for row in keyboard.inline_keyboard:
|
|
||||||
line = []
|
|
||||||
for button in row:
|
|
||||||
button = ntb(button) # btn() format
|
|
||||||
line.append(button)
|
|
||||||
lines.append(line)
|
|
||||||
return lines
|
|
||||||
# return ikb() format
|
|
||||||
|
|
||||||
|
|
||||||
def ntb(button):
|
|
||||||
for btn_type in ['callback_data', 'url', 'switch_inline_query', 'switch_inline_query_current_chat',
|
|
||||||
'callback_game']:
|
|
||||||
value = getattr(button, btn_type)
|
|
||||||
if value:
|
|
||||||
break
|
|
||||||
button = [button.text, value]
|
|
||||||
if btn_type != 'callback_data':
|
|
||||||
button.append(btn_type)
|
|
||||||
return button
|
|
||||||
# return {'text': text, type: value}
|
|
||||||
|
|
||||||
|
|
||||||
def kb(rows=None, **kwargs):
|
|
||||||
if rows is None:
|
|
||||||
rows = []
|
|
||||||
lines = []
|
|
||||||
for row in rows:
|
|
||||||
line = []
|
|
||||||
for button in row:
|
|
||||||
button_type = type(button)
|
|
||||||
if button_type == str:
|
|
||||||
button = KeyboardButton(button)
|
|
||||||
elif button_type == dict:
|
|
||||||
button = KeyboardButton(**button)
|
|
||||||
|
|
||||||
line.append(button)
|
|
||||||
lines.append(line)
|
|
||||||
return ReplyKeyboardMarkup(keyboard=lines, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
kbtn = KeyboardButton
|
|
||||||
|
|
||||||
|
|
||||||
def force_reply(selective=True):
|
|
||||||
return ForceReply(selective=selective)
|
|
||||||
|
|
||||||
|
|
||||||
def array_chunk(input_, size):
|
|
||||||
return [input_[i:i + size] for i in range(0, len(input_), size)]
|
|
0
pyromod/methods/__init__.py
Normal file
0
pyromod/methods/__init__.py
Normal file
127
pyromod/methods/sign_in_qrcode.py
Normal file
127
pyromod/methods/sign_in_qrcode.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import asyncio
|
||||||
|
import base64
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
from pyqrcode import QRCode
|
||||||
|
from pyrogram import Client
|
||||||
|
from pyrogram.errors import SessionPasswordNeeded, BadRequest
|
||||||
|
from pyrogram.session import Auth, Session
|
||||||
|
from pyrogram.utils import ainput
|
||||||
|
|
||||||
|
from pagermaid import Config
|
||||||
|
|
||||||
|
|
||||||
|
async def sign_in_qrcode(
|
||||||
|
client: Client,
|
||||||
|
) -> Optional[str]:
|
||||||
|
req = await client.invoke(
|
||||||
|
pyrogram.raw.functions.auth.ExportLoginToken(
|
||||||
|
api_id=client.api_id,
|
||||||
|
api_hash=client.api_hash,
|
||||||
|
except_ids=[],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(req, pyrogram.raw.types.auth.LoginToken):
|
||||||
|
token = base64.b64encode(req.token)
|
||||||
|
return f"tg://login?token={token.decode('utf-8')}"
|
||||||
|
elif isinstance(req, pyrogram.raw.types.auth.LoginTokenMigrateTo):
|
||||||
|
await client.session.stop()
|
||||||
|
await client.storage.dc_id(req.dc_id)
|
||||||
|
await client.storage.auth_key(
|
||||||
|
await Auth(client, await client.storage.dc_id(), await client.storage.test_mode()).create()
|
||||||
|
)
|
||||||
|
client.session = Session(
|
||||||
|
client, await client.storage.dc_id(),
|
||||||
|
await client.storage.auth_key(), await client.storage.test_mode()
|
||||||
|
)
|
||||||
|
await client.session.start()
|
||||||
|
req = await client.invoke(pyrogram.raw.functions.auth.ImportLoginToken(token=req.token))
|
||||||
|
await client.storage.user_id(req.user.id)
|
||||||
|
await client.storage.is_bot(False)
|
||||||
|
return pyrogram.types.User._parse(client, req.user)
|
||||||
|
elif isinstance(req, pyrogram.raw.types.auth.LoginTokenSuccess):
|
||||||
|
await client.storage.user_id(req.authorization.user.id)
|
||||||
|
await client.storage.is_bot(False)
|
||||||
|
return pyrogram.types.User._parse(client, req.authorization.user)
|
||||||
|
|
||||||
|
|
||||||
|
async def authorize_by_qrcode(
|
||||||
|
client: Client,
|
||||||
|
):
|
||||||
|
print(f"Welcome to Pyrogram (version {pyrogram.__version__})")
|
||||||
|
print(f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
|
||||||
|
f"under the terms of the {pyrogram.__license__}.\n")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
qrcode = None
|
||||||
|
try:
|
||||||
|
qrcode = await sign_in_qrcode(client)
|
||||||
|
except BadRequest as e:
|
||||||
|
print(e.MESSAGE)
|
||||||
|
except SessionPasswordNeeded as e:
|
||||||
|
print(e.MESSAGE)
|
||||||
|
while True:
|
||||||
|
print(f"Password hint: {await client.get_password_hint()}")
|
||||||
|
|
||||||
|
if not client.password:
|
||||||
|
client.password = await ainput("Enter password (empty to recover): ", hide=client.hide_password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if client.password:
|
||||||
|
return await client.check_password(client.password)
|
||||||
|
confirm = await ainput("Confirm password recovery (y/n): ")
|
||||||
|
|
||||||
|
if confirm == "y":
|
||||||
|
email_pattern = await client.send_recovery_code()
|
||||||
|
print(f"The recovery code has been sent to {email_pattern}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
recovery_code = await ainput("Enter recovery code: ")
|
||||||
|
|
||||||
|
try:
|
||||||
|
return await client.recover_password(recovery_code)
|
||||||
|
except BadRequest as e:
|
||||||
|
print(e.MESSAGE)
|
||||||
|
else:
|
||||||
|
client.password = None
|
||||||
|
except BadRequest as e:
|
||||||
|
print(e.MESSAGE)
|
||||||
|
client.password = None
|
||||||
|
if isinstance(qrcode, str):
|
||||||
|
qr_obj = QRCode(qrcode)
|
||||||
|
try:
|
||||||
|
qr_obj.png("qrcode.png", scale=6)
|
||||||
|
except Exception:
|
||||||
|
print("Save qrcode.png failed.")
|
||||||
|
print(qr_obj.terminal())
|
||||||
|
print(f"Scan the QR code above, the qrcode.png file or visit {qrcode} to log in.\n")
|
||||||
|
print("QR code will expire in 20 seconds. If you have scanned it, please wait...")
|
||||||
|
await asyncio.sleep(20)
|
||||||
|
elif isinstance(qrcode, pyrogram.types.User):
|
||||||
|
return qrcode
|
||||||
|
|
||||||
|
|
||||||
|
async def start_client(client: Client):
|
||||||
|
is_authorized = await client.connect()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not is_authorized:
|
||||||
|
if Config.QRCODE_LOGIN:
|
||||||
|
await authorize_by_qrcode(client)
|
||||||
|
else:
|
||||||
|
await client.authorize()
|
||||||
|
|
||||||
|
if not await client.storage.is_bot() and client.takeout:
|
||||||
|
client.takeout_id = (await client.invoke(pyrogram.raw.functions.account.InitTakeoutSession())).id
|
||||||
|
|
||||||
|
await client.invoke(pyrogram.raw.functions.updates.GetState())
|
||||||
|
except (Exception, KeyboardInterrupt):
|
||||||
|
await client.disconnect()
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
client.me = await client.get_me()
|
||||||
|
await client.initialize()
|
||||||
|
|
||||||
|
return client
|
@ -1,20 +0,0 @@
|
|||||||
"""
|
|
||||||
pyromod - A monkeypatcher add-on for Pyrogram
|
|
||||||
Copyright (C) 2020 Cezar H. <https://github.com/usernein>
|
|
||||||
|
|
||||||
This file is part of pyromod.
|
|
||||||
|
|
||||||
pyromod is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
pyromod 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 General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
from .pagination import Pagination
|
|
@ -1,91 +0,0 @@
|
|||||||
"""
|
|
||||||
pyromod - A monkeypatcher add-on for Pyrogram
|
|
||||||
Copyright (C) 2020 Cezar H. <https://github.com/usernein>
|
|
||||||
|
|
||||||
This file is part of pyromod.
|
|
||||||
|
|
||||||
pyromod is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
pyromod 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 General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
import math
|
|
||||||
from ..helpers import array_chunk
|
|
||||||
|
|
||||||
|
|
||||||
class Pagination:
|
|
||||||
def __init__(self, objects, page_data=None, item_data=None, item_title=None):
|
|
||||||
default_page_callback = (lambda x: str(x))
|
|
||||||
default_item_callback = (lambda i, pg: f'[{pg}] {i}')
|
|
||||||
self.objects = objects
|
|
||||||
self.page_data = page_data or default_page_callback
|
|
||||||
self.item_data = item_data or default_item_callback
|
|
||||||
self.item_title = item_title or default_item_callback
|
|
||||||
|
|
||||||
def create(self, page, lines=5, columns=1):
|
|
||||||
quant_per_page = lines * columns
|
|
||||||
page = 1 if page <= 0 else page
|
|
||||||
offset = (page - 1) * quant_per_page
|
|
||||||
stop = offset + quant_per_page
|
|
||||||
cutted = self.objects[offset:stop]
|
|
||||||
|
|
||||||
total = len(self.objects)
|
|
||||||
pages_range = [*range(1, math.ceil(total / quant_per_page) + 1)] # each item is a page
|
|
||||||
last_page = len(pages_range)
|
|
||||||
|
|
||||||
nav = []
|
|
||||||
if page <= 3:
|
|
||||||
for n in [1, 2, 3]:
|
|
||||||
if n not in pages_range:
|
|
||||||
continue
|
|
||||||
text = f"· {n} ·" if n == page else n
|
|
||||||
nav.append((text, self.page_data(n)))
|
|
||||||
if last_page >= 4:
|
|
||||||
nav.append(
|
|
||||||
('4 ›' if last_page > 5 else 4, self.page_data(4))
|
|
||||||
)
|
|
||||||
if last_page > 4:
|
|
||||||
nav.append(
|
|
||||||
(f'{last_page} »' if last_page > 5 else last_page, self.page_data(last_page))
|
|
||||||
)
|
|
||||||
elif page >= last_page - 2:
|
|
||||||
nav.extend(
|
|
||||||
[
|
|
||||||
('« 1' if last_page > 5 else 1, self.page_data(1)),
|
|
||||||
(
|
|
||||||
f'‹ {last_page - 3}' if last_page > 5 else last_page - 3,
|
|
||||||
self.page_data(last_page - 3),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
for n in range(last_page - 2, last_page + 1):
|
|
||||||
text = f"· {n} ·" if n == page else n
|
|
||||||
nav.append((text, self.page_data(n)))
|
|
||||||
else:
|
|
||||||
nav = [
|
|
||||||
('« 1', self.page_data(1)),
|
|
||||||
(f'‹ {page - 1}', self.page_data(page - 1)),
|
|
||||||
(f'· {page} ·', "noop"),
|
|
||||||
(f'{page + 1} ›', self.page_data(page + 1)),
|
|
||||||
(f'{last_page} »', self.page_data(last_page)),
|
|
||||||
]
|
|
||||||
|
|
||||||
buttons = [
|
|
||||||
(self.item_title(item, page), self.item_data(item, page))
|
|
||||||
for item in cutted
|
|
||||||
]
|
|
||||||
|
|
||||||
kb_lines = array_chunk(buttons, columns)
|
|
||||||
if last_page > 1:
|
|
||||||
kb_lines.append(nav)
|
|
||||||
|
|
||||||
return kb_lines
|
|
@ -10,3 +10,5 @@ apscheduler
|
|||||||
sqlitedict
|
sqlitedict
|
||||||
casbin==1.17.5
|
casbin==1.17.5
|
||||||
sentry-sdk==1.13.0
|
sentry-sdk==1.13.0
|
||||||
|
PyQRCode>=1.2.1
|
||||||
|
PyPng
|
||||||
|
@ -19,7 +19,10 @@ configure () {
|
|||||||
printf "请输入应用程序 api_hash(不懂请直接回车):"
|
printf "请输入应用程序 api_hash(不懂请直接回车):"
|
||||||
read -r api_hash <&1
|
read -r api_hash <&1
|
||||||
sed -i "s/HASH_HERE/$api_hash/" $config_file
|
sed -i "s/HASH_HERE/$api_hash/" $config_file
|
||||||
printf "请输入应用程序语言(默认:zh-cn):"
|
read -p "二维码扫码登录?(避免无法收到验证码) [Y/n]" choi
|
||||||
|
if [ "$choi" == "y" ] || [ "$choi" == "Y" ]; then
|
||||||
|
sed -i "s/qrcode_login: \"False\"/qrcode_login: \"True\"/" $config_file
|
||||||
|
fi
|
||||||
read -r application_language <&1
|
read -r application_language <&1
|
||||||
if [ -z "$application_language" ]
|
if [ -z "$application_language" ]
|
||||||
then
|
then
|
||||||
|
Loading…
Reference in New Issue
Block a user