🔖 Update to v1.4.0

Support web login
This commit is contained in:
omg-xtao 2023-06-20 17:02:18 +08:00 committed by GitHub
parent 0cb44d621d
commit 8e29af1875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 504 additions and 94 deletions

View File

@ -11,6 +11,7 @@
api_id: "ID_HERE" api_id: "ID_HERE"
api_hash: "HASH_HERE" api_hash: "HASH_HERE"
qrcode_login: "False" qrcode_login: "False"
web_login: "False"
# Either debug logging is enabled or not # Either debug logging is enabled or not
debug: "False" debug: "False"

View File

@ -21,8 +21,8 @@ from pagermaid.scheduler import scheduler
import pyromod.listen import pyromod.listen
from pyrogram import Client from pyrogram import Client
pgm_version = "1.3.4" pgm_version = "1.4.0"
pgm_version_code = 1304 pgm_version_code = 1400
CMD_LIST = {} CMD_LIST = {}
module_dir = __path__[0] module_dir = __path__[0]
working_dir = getcwd() working_dir = getcwd()

View File

@ -1,15 +1,16 @@
import asyncio import asyncio
from os import sep from os import sep
from signal import signal as signal_fn, SIGINT, SIGTERM, SIGABRT from signal import signal as signal_fn, SIGINT, SIGTERM, SIGABRT
from sys import path, platform from sys import path, platform, exit
from pyrogram.errors import AuthKeyUnregistered from pyrogram.errors import AuthKeyUnregistered
from pagermaid import bot, logs, working_dir from pagermaid import bot, logs, working_dir, Config
from pagermaid.common.reload import load_all from pagermaid.common.reload import load_all
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 pagermaid.web import web from pagermaid.web import web
from pagermaid.web.api.web_login import web_login
from pyromod.methods.sign_in_qrcode import start_client 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")
@ -50,11 +51,29 @@ async def console_bot():
await process_exit(start=True, _client=bot) await process_exit(start=True, _client=bot)
async def web_bot():
try:
await web_login.init()
except AuthKeyUnregistered:
safe_remove("pagermaid.session")
exit()
if bot.me is not None:
me = await bot.get_me()
if me.is_bot:
safe_remove("pagermaid.session")
exit()
else:
logs.info("Please use web to login, path: web_login .")
async def main(): async def main():
logs.info(lang("platform") + platform + lang("platform_load")) logs.info(lang("platform") + platform + lang("platform_load"))
await web.start() await web.start()
await console_bot() if not (Config.WEB_ENABLE and Config.WEB_LOGIN):
logs.info(lang("start")) await console_bot()
logs.info(lang("start"))
else:
await web_bot()
try: try:
await idle() await idle()

View File

@ -48,6 +48,9 @@ class Config:
QRCODE_LOGIN = strtobool( QRCODE_LOGIN = strtobool(
os.environ.get("QRCODE_LOGIN", config.get("qrcode_login", "false")) os.environ.get("QRCODE_LOGIN", config.get("qrcode_login", "false"))
) )
WEB_LOGIN = strtobool(
os.environ.get("WEB_LOGIN", config.get("web_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( ERROR_REPORT = strtobool(

View File

@ -5,3 +5,13 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
from sqlitedict import SqliteDict from sqlitedict import SqliteDict
from httpx import AsyncClient from httpx import AsyncClient
from logging import Logger from logging import Logger
__all__ = [
"Client",
"Message",
"Sub",
"AsyncIOScheduler",
"SqliteDict",
"AsyncClient",
"Logger"
]

View File

@ -34,6 +34,7 @@ from pagermaid.utils import (
process_exit, process_exit,
) )
from pagermaid.hook import Hook from pagermaid.hook import Hook
from pagermaid.web import web
_lock = asyncio.Lock() _lock = asyncio.Lock()
@ -197,7 +198,7 @@ def listener(**args):
except SystemExit: except SystemExit:
await process_exit(start=False, _client=client, message=message) await process_exit(start=False, _client=client, message=message)
await Hook.shutdown() await Hook.shutdown()
sys.exit(0) web.stop()
except BaseException: except BaseException:
exc_info = sys.exc_info()[1] exc_info = sys.exc_info()[1]
exc_format = format_exc() exc_format = format_exc()

View File

@ -1,7 +1,5 @@
""" The help module. """ """ The help module. """
from os import listdir
from json import dump as json_dump
from os import listdir, sep
from pyrogram.enums import ParseMode from pyrogram.enums import ParseMode

View File

@ -140,7 +140,7 @@ async def plugin(message: Message):
for target_plugin in active_plugins: for target_plugin in active_plugins:
inactive_plugins.remove(target_plugin) inactive_plugins.remove(target_plugin)
chdir(f"plugins{sep}") chdir(f"plugins{sep}")
for target_plugin in glob(f"*.py.disabled"): for target_plugin in glob("*.py.disabled"):
disabled_plugins += [f"{target_plugin[:-12]}"] disabled_plugins += [f"{target_plugin[:-12]}"]
chdir(f"..{sep}") chdir(f"..{sep}")
active_plugins_string = "" active_plugins_string = ""

View File

@ -1,6 +1,7 @@
import sentry_sdk import sentry_sdk
from subprocess import run, PIPE from subprocess import run, PIPE
import sys
from time import time from time import time
from pyrogram.errors import Unauthorized, UsernameInvalid from pyrogram.errors import Unauthorized, UsernameInvalid
@ -18,7 +19,7 @@ def sentry_before_send(event, hint):
if exc_info and isinstance(exc_info[1], (Unauthorized, UsernameInvalid)): if exc_info and isinstance(exc_info[1], (Unauthorized, UsernameInvalid)):
# The user has been deleted/deactivated or session revoked # The user has been deleted/deactivated or session revoked
safe_remove("pagermaid.session") safe_remove("pagermaid.session")
exit(1) sys.exit(1)
if time() <= sentry_sdk_report_time + 30: if time() <= sentry_sdk_report_time + 30:
sentry_sdk_report_time = time() sentry_sdk_report_time = time()
return None return None
@ -29,7 +30,7 @@ def sentry_before_send(event, hint):
sentry_sdk_report_time = time() sentry_sdk_report_time = time()
sentry_sdk_git_hash = ( sentry_sdk_git_hash = (
run("git rev-parse HEAD", stdout=PIPE, shell=True).stdout.decode().strip() run("git rev-parse HEAD", stdout=PIPE, shell=True, check=True).stdout.decode(check=True).strip(check=True)
) )
sentry_sdk.init( sentry_sdk.init(
Config.SENTRY_API, Config.SENTRY_API,

View File

@ -2,7 +2,7 @@
import re import re
from datetime import datetime, timezone from datetime import datetime
from platform import uname, python_version from platform import uname, python_version
from sys import platform from sys import platform
@ -18,7 +18,7 @@ from psutil import boot_time, virtual_memory, disk_partitions
from shutil import disk_usage from shutil import disk_usage
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from pagermaid import start_time, Config, pgm_version from pagermaid import Config, pgm_version
from pagermaid.common.status import get_bot_uptime from pagermaid.common.status import get_bot_uptime
from pagermaid.enums import Client, Message from pagermaid.enums import Client, Message
from pagermaid.listener import listener from pagermaid.listener import listener

View File

@ -1,3 +1,4 @@
import sys
from getpass import getuser from getpass import getuser
from os.path import exists, sep from os.path import exists, sep
from platform import node from platform import node
@ -8,7 +9,6 @@ from pagermaid.common.system import run_eval
from pagermaid.enums import Message from pagermaid.enums import Message
from pagermaid.listener import listener from pagermaid.listener import listener
from pagermaid.utils import attach_log, execute, lang, upload_attachment from pagermaid.utils import attach_log, execute, lang, upload_attachment
from pagermaid.web import web
@listener( @listener(
@ -51,7 +51,7 @@ async def restart(message: Message):
"""To re-execute PagerMaid.""" """To re-execute PagerMaid."""
if not message.text[0].isalpha(): if not message.text[0].isalpha():
await message.edit(lang("restart_log")) await message.edit(lang("restart_log"))
web.stop() sys.exit(0)
@listener( @listener(
@ -65,7 +65,8 @@ async def sh_eval(message: Message):
"""Run python commands from Telegram.""" """Run python commands from Telegram."""
dev_mode = exists(f"data{sep}dev") dev_mode = exists(f"data{sep}dev")
try: try:
assert dev_mode if not dev_mode:
raise AssertionError
cmd = message.text.split(" ", maxsplit=1)[1] cmd = message.text.split(" ", maxsplit=1)[1]
except (IndexError, AssertionError): except (IndexError, AssertionError):
return await message.edit(lang("eval_need_dev")) return await message.edit(lang("eval_need_dev"))

View File

@ -4,6 +4,14 @@ from pagermaid.single_utils import sqlite
from pagermaid.scheduler import scheduler from pagermaid.scheduler import scheduler
from pagermaid.utils import client from pagermaid.utils import client
__all__ = [
"bot",
"logs",
"sqlite",
"scheduler",
"client",
]
def get(name: str): def get(name: str):
data = { data = {
@ -13,4 +21,4 @@ def get(name: str):
"AsyncIOScheduler": scheduler, "AsyncIOScheduler": scheduler,
"AsyncClient": client, "AsyncClient": client,
} }
return data.get(name, None) return data.get(name)

View File

@ -3,7 +3,6 @@ from os import sep, remove, mkdir
from os.path import exists from os.path import exists
from typing import List, Optional, Union from typing import List, Optional, Union
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from httpx import AsyncClient
from pyrogram import Client as OldClient from pyrogram import Client as OldClient
from pyrogram.types import Chat as OldChat, Message as OldMessage, Dialog from pyrogram.types import Chat as OldChat, Message as OldMessage, Dialog
@ -14,9 +13,13 @@ from pyromod.utils.errors import (
TimeoutConversationError, TimeoutConversationError,
ListenerCanceled, ListenerCanceled,
) )
from sqlitedict import SqliteDict from sqlitedict import SqliteDict
__all__ = [
"AlreadyInConversationError",
"TimeoutConversationError",
"ListenerCanceled",
]
# init folders # init folders
if not exists("data"): if not exists("data"):
mkdir("data") mkdir("data")

View File

@ -10,7 +10,7 @@ from sys import executable
from asyncio import create_subprocess_shell, sleep from asyncio import create_subprocess_shell, sleep
from asyncio.subprocess import PIPE from asyncio.subprocess import PIPE
from pyrogram import filters, enums from pyrogram import filters
from pagermaid.config import Config from pagermaid.config import Config
from pagermaid import bot from pagermaid import bot
from pagermaid.group_manager import enforce_permission from pagermaid.group_manager import enforce_permission

View File

@ -7,7 +7,7 @@ from starlette.responses import RedirectResponse
from pagermaid import logs from pagermaid import logs
from pagermaid.config import Config from pagermaid.config import Config
from pagermaid.web.api import base_api_router from pagermaid.web.api import base_api_router, base_html_router
from pagermaid.web.pages import admin_app, login_page from pagermaid.web.pages import admin_app, login_page
requestAdaptor = """ requestAdaptor = """
@ -39,6 +39,7 @@ class Web:
def init_web(self): def init_web(self):
self.app.include_router(base_api_router) self.app.include_router(base_api_router)
self.app.include_router(base_html_router)
self.app.add_middleware( self.app.add_middleware(
CORSMiddleware, CORSMiddleware,

View File

@ -7,8 +7,12 @@ from pagermaid.web.api.ignore_groups import route as ignore_groups_route
from pagermaid.web.api.login import route as login_route from pagermaid.web.api.login import route as login_route
from pagermaid.web.api.plugin import route as plugin_route from pagermaid.web.api.plugin import route as plugin_route
from pagermaid.web.api.status import route as status_route from pagermaid.web.api.status import route as status_route
from pagermaid.web.api.web_login import route as web_login_route, html_route as web_login_html_route
__all__ = ["authentication", "base_api_router", "base_html_router"]
base_api_router = APIRouter(prefix="/pagermaid/api") base_api_router = APIRouter(prefix="/pagermaid/api")
base_html_router = APIRouter()
base_api_router.include_router(plugin_route) base_api_router.include_router(plugin_route)
base_api_router.include_router(bot_info_route) base_api_router.include_router(bot_info_route)
@ -16,3 +20,5 @@ base_api_router.include_router(status_route)
base_api_router.include_router(login_route) base_api_router.include_router(login_route)
base_api_router.include_router(command_alias_route) base_api_router.include_router(command_alias_route)
base_api_router.include_router(ignore_groups_route) base_api_router.include_router(ignore_groups_route)
base_api_router.include_router(web_login_route)
base_html_router.include_router(web_login_html_route)

View File

@ -0,0 +1,125 @@
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from pyrogram.errors import BadRequest, AuthTokenExpired
from pyrogram.raw.functions.account import InitTakeoutSession
from pyrogram.raw.functions.updates import GetState
from starlette.responses import HTMLResponse
from pagermaid import bot, logs
from pagermaid.common.reload import load_all
from pagermaid.utils import lang, process_exit
from pagermaid.web.api import authentication
from pagermaid.web.html import get_web_login_html
from pyromod.methods.sign_in_qrcode import authorize_by_qrcode_web
from pyromod.utils.errors import QRCodeWebCodeError, QRCodeWebNeedPWDError
import sys
class UserModel(BaseModel):
password: str
class WebLogin():
def __init__(self):
self.is_authorized = False
self.need_password = False
self.password_hint = ""
async def connect(self):
if not bot.is_connected:
self.is_authorized = await bot.connect()
@staticmethod
async def initialize():
if bot.is_connected and not bot.is_initialized:
await bot.initialize()
@staticmethod
def has_login():
return bot.me is not None
@staticmethod
async def init_bot():
logs.info(f"{lang('save_id')} {bot.me.first_name}({bot.me.id})")
await load_all()
await process_exit(start=True, _client=bot)
logs.info(lang("start"))
async def init(self):
await self.connect()
if not self.is_authorized:
return
if not await bot.storage.is_bot() and bot.takeout:
bot.takeout_id = (
await bot.invoke(InitTakeoutSession())
).id
await bot.invoke(GetState())
bot.me = await bot.get_me()
if bot.me.is_bot:
sys.exit(0)
await self.initialize()
await self.init_bot()
route = APIRouter()
html_route = APIRouter()
web_login = WebLogin()
web_login_html = get_web_login_html()
@route.get("/web_login", response_class=JSONResponse, dependencies=[authentication()])
async def web_login_qrcode():
if web_login.has_login():
return {"status": 0, "msg": "已登录"}
try:
await web_login.connect()
if not web_login.is_authorized:
await authorize_by_qrcode_web(bot)
web_login.is_authorized = True
await web_login.init()
return {"status": 0, "msg": "登录成功"}
except QRCodeWebCodeError as e:
web_login.need_password = False
return {"status": 1, "msg": "未扫码", "content": e.code}
except QRCodeWebNeedPWDError as e:
web_login.need_password = True
web_login.password_hint = e.hint or ""
return {"status": 2, "msg": "需要密码", "content": web_login.password_hint}
except AuthTokenExpired:
return {"status": 3, "msg": "登录状态过期,请重新扫码登录"}
except BadRequest as e:
return {"status": 3, "msg": e.MESSAGE}
except Exception as e:
return {"status": 3, "msg": f"{type(e)}"}
@route.post("/web_login", response_class=JSONResponse, dependencies=[authentication()])
async def web_login_password(user: UserModel):
if web_login.has_login():
return {"status": 0, "msg": "已登录"}
if not web_login.need_password:
return {"status": 0, "msg": "无需密码"}
try:
await authorize_by_qrcode_web(bot, user.password)
web_login.is_authorized = True
await web_login.init()
return {"status": 0, "msg": "登录成功"}
except QRCodeWebCodeError as e:
return {"status": 1, "msg": "需要重新扫码", "content": e.code}
except QRCodeWebNeedPWDError as e:
web_login.need_password = True
return {"status": 2, "msg": "密码错误", "content": e.hint or ""}
except AuthTokenExpired:
web_login.need_password = False
return {"status": 3, "msg": "登录状态过期,请重新扫码登录"}
except BadRequest as e:
return {"status": 3, "msg": e.MESSAGE}
except Exception as e:
return {"status": 3, "msg": f"{type(e)}"}
@html_route.get("/web_login", response_class=HTMLResponse)
async def get_web_login():
return web_login_html

View File

@ -22,3 +22,8 @@ def get_github_logo() -> str:
def get_footer() -> str: def get_footer() -> str:
"""获取 footer。""" """获取 footer。"""
return get_html(html_base_path / "footer.html") return get_html(html_base_path / "footer.html")
def get_web_login_html() -> str:
"""获取 web login。"""
return get_html(html_base_path / "web_login.html")

View File

@ -0,0 +1,202 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" integrity="sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.min.js" integrity="sha512-3dZ9wIrMMij8rOH7X3kLfXAzwtcHpuYpEgQg1OA4QAob1e81H8ntUQmQm3pBudqIoySO5j0tHN4ENzA6+n2r4w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.4.0/axios.min.js" integrity="sha512-uMtXmF28A2Ab/JJO2t/vYhlaa/3ahUOgj1Zf27M5rOo8/+fcTUVH0/E0ll68njmjrLqOBjXM3V9NiPFL5ywWPQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<title>QR Code Login - PagerMaid-Pyro</title>
<style>
img {
display: inline-block!important;
}
</style>
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-4">
<div class="card" style="">
<div class="card-header bg-dark text-white text-center">
<h3 class="mb-0">QR Code Login</h3>
</div>
<div class="card-body" style="display: block" id="qrDiv">
<div id="qr" class="text-center mb-0"></div>
<p class="text-center mb-0">Scan the QR code above to log in.</p>
</div>
<div style="margin-left: 10%; width: 80%; display: block" class="text-center mb-3" id="pwdDiv">
<form id="pwdForm">
<div class="mb-3" id="pwdHint">
<label for="hint" class="form-label">Hint</label>
<textarea class="form-control" id="hint" name="hint" disabled></textarea>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="confirm-password" class="form-label">Confirm Password</label>
<input type="password" class="form-control" id="confirm-password" name="confirm-password" required>
</div>
<button type="submit" class="btn btn-primary" >Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- 模态框 -->
<div class="modal" id="errorModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- 模态框头部 -->
<div class="modal-header">
<h4 class="modal-title">Notice</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<!-- 模态框内容 -->
<div class="modal-body" id="errorText"></div>
<!-- 模态框底部 -->
<div class="modal-footer">
<button type="button" class="btn" data-bs-dismiss="modal" id="errorButton">我知道了</button>
</div>
</div>
</div>
</div>
<script>
const QRDiv = document.getElementById('qrDiv');
const QRC = document.getElementById('qr');
const QR = new QRCode(QRC);
const PWDDiv = document.getElementById('pwdDiv');
const errorModel = new bootstrap.Modal(document.getElementById('errorModal'));
const errorText = document.getElementById('errorText');
const errorButton = document.getElementById('errorButton');
const pwdHint = document.getElementById('pwdHint');
const hint = document.getElementById('hint');
const password = document.getElementById('password');
const confirmPassword = document.getElementById('confirm-password');
function getHeader() {
return {
headers: {
token: localStorage.getItem("token") || ""
}
}
}
function redirectToLogin() {
window.location.href = '/admin';
}
function showError(text) {
if (errorButton.classList.contains('btn-success')) {
errorButton.classList.remove('btn-success');
}
errorButton.classList.add('btn-danger');
errorText.innerHTML = text;
errorModel.show();
}
function showSuccess(text) {
if (errorButton.classList.contains('btn-danger')) {
errorButton.classList.remove('btn-danger');
}
errorButton.classList.add('btn-success');
errorText.innerHTML = text;
errorModel.show();
}
function process_data(data) {
switch (data.status) {
case 0:
showSuccess(data.msg);
redirectToLogin();
break;
case 1:
QRDiv.style.display = 'block';
PWDDiv.style.display = 'none';
QR.makeCode(data.content);
break;
case 2:
QRDiv.style.display = 'none';
PWDDiv.style.display = 'block';
if (data.content !== '') {
pwdHint.style.display = 'block';
hint.innerHTML = data.content;
} else {
pwdHint.style.display = 'none';
}
break;
case 3:
showError(data.msg);
break;
default:
showError('未知错误');
}
}
function getQrCode() {
if (PWDDiv.style.display === 'block') {
return;
}
axios.get('pagermaid/api/web_login', getHeader())
.then(function (response) {
const data = response.data;
console.log(data);
process_data(data);
})
.catch(function (error) {
if (error.response.status !== 200) {
redirectToLogin();
return;
}
console.log(error);
showError(error.message);
});
}
function submitPwd() {
const pwd = password.value;
const confirmPwd = confirmPassword.value;
if (pwd !== confirmPwd) {
showError('两次输入的密码不一致');
return;
}
axios.post('pagermaid/api/web_login', {
password: pwd
}, getHeader())
.then(function (response) {
const data = response.data;
console.log(data);
process_data(data);
})
.catch(function (error) {
if (error.response.status !== 200) {
redirectToLogin();
return;
}
console.log(error);
showError(error.message);
});
}
PWDDiv.style.display = 'none';
document.getElementById("pwdForm").addEventListener('submit', function (e) {
e.preventDefault();
submitPwd();
});
getQrCode()
setInterval(getQrCode, 20000);
</script>
</body>
</html>

View File

@ -1,2 +1,4 @@
from .login import login_page from .login import login_page
from .main import admin_app, blank_page from .main import admin_app, blank_page
__all__ = ["admin_app", "blank_page", "login_page"]

View File

@ -2,13 +2,10 @@ from amis import (
InputText, InputText,
Switch, Switch,
Card, Card,
Tpl,
CardsCRUD, CardsCRUD,
PageSchema, PageSchema,
Page, Page,
Button, )
Select,
)
card = Card( card = Card(
header=Card.Header( header=Card.Header(

View File

@ -1,4 +1,4 @@
from amis import InputText, Switch, Card, Tpl, CardsCRUD, PageSchema, Page, Button from amis import InputText, Switch, Card, CardsCRUD, PageSchema, Page
card = Card( card = Card(
header=Card.Header( header=Card.Header(

View File

@ -1,21 +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 .filters import dice

View File

@ -1,28 +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 pyrogram
def dice(ctx, message):
return hasattr(message, "dice") and message.dice
pyrogram.filters.dice = dice

View File

@ -17,5 +17,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with pyromod. If not, see <https://www.gnu.org/licenses/>. along with pyromod. If not, see <https://www.gnu.org/licenses/>.
""" """
from .listen import Client, MessageHandler, Chat, User from .listen import Client, MessageHandler, Chat, User
__all__ = ["Client", "MessageHandler", "Chat", "User"]

View File

@ -27,7 +27,7 @@ from typing import Optional, List, Union
import pyrogram import pyrogram
from pyrogram.enums import ChatType from pyrogram.enums import ChatType
from pagermaid.single_utils import get_sudo_list, Message from pagermaid.single_utils import get_sudo_list
from pagermaid.scheduler import add_delete_message_job from pagermaid.scheduler import add_delete_message_job
from ..methods.get_dialogs_list import get_dialogs_list as get_dialogs_list_func from ..methods.get_dialogs_list import get_dialogs_list as get_dialogs_list_func
from ..methods.read_chat_history import read_chat_history as read_chat_history_func from ..methods.read_chat_history import read_chat_history as read_chat_history_func

View File

@ -10,6 +10,7 @@ from pyrogram.session import Auth, Session
from pyrogram.utils import ainput from pyrogram.utils import ainput
from pagermaid import Config from pagermaid import Config
from pyromod.utils.errors import QRCodeWebNeedPWDError, QRCodeWebCodeError
async def sign_in_qrcode( async def sign_in_qrcode(
@ -117,6 +118,32 @@ async def authorize_by_qrcode(
return qrcode return qrcode
async def authorize_by_qrcode_web(
client: Client,
password: Optional[str] = None,
):
qrcode = None
try:
if password:
client.password = password
raise SessionPasswordNeeded()
qrcode = await sign_in_qrcode(client)
except BadRequest as e:
raise e
except SessionPasswordNeeded as e:
try:
if client.password:
return await client.check_password(client.password)
except BadRequest as e:
client.password = None
raise e
raise QRCodeWebNeedPWDError(await client.get_password_hint()) from e
if isinstance(qrcode, str):
raise QRCodeWebCodeError(qrcode)
elif isinstance(qrcode, pyrogram.types.User):
return qrcode
async def start_client(client: Client): async def start_client(client: Client):
is_authorized = await client.connect() is_authorized = await client.connect()

View File

@ -17,5 +17,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with pyromod. If not, see <https://www.gnu.org/licenses/>. along with pyromod. If not, see <https://www.gnu.org/licenses/>.
""" """
from .utils import patch, patchable from .utils import patch, patchable
__all__ = ["patch", "patchable"]

View File

@ -26,3 +26,33 @@ class ListenerCanceled(Exception):
def __init__(self): def __init__(self):
super().__init__("Listener was canceled") super().__init__("Listener was canceled")
class QRCodeWebError(Exception):
"""
Occurs when the QR code is not scanned.
"""
def __init__(self, msg: str):
self.msg = msg
super().__init__("QR code not scanned")
class QRCodeWebCodeError(QRCodeWebError):
"""
Occurs when the QR code is not scanned.
"""
def __init__(self, code: str):
self.code = code
super().__init__("QR code not scanned")
class QRCodeWebNeedPWDError(QRCodeWebError):
"""
Occurs when the account needs to be verified.
"""
def __init__(self, hint: str):
self.hint = hint
super().__init__("Account needs to be verified")

View File

@ -20,7 +20,8 @@ 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
read -p "二维码扫码登录?(避免无法收到验证码) [Y/n]" choi printf "控制台二维码扫码登录?(避免无法收到验证码) [Y/n]"
read -r choi <&1
if [ "$choi" == "y" ] || [ "$choi" == "Y" ]; then if [ "$choi" == "y" ] || [ "$choi" == "Y" ]; then
sed -i "s/qrcode_login: \"False\"/qrcode_login: \"True\"/" $config_file sed -i "s/qrcode_login: \"False\"/qrcode_login: \"True\"/" $config_file
fi fi
@ -40,29 +41,22 @@ configure () {
else else
sed -i "s/China/$application_region/" $config_file sed -i "s/China/$application_region/" $config_file
fi fi
printf "请输入 Google TTS 语言默认zh-CN"
read -r application_tts <&1
if [ -z "$application_tts" ]
then
echo "tts发音语言设置为 简体中文"
else
sed -i "s/zh-CN/$application_tts/" $config_file
fi
} }
login () { login () {
echo echo
echo "下面进行程序运行。" echo "下面进行程序运行。"
echo "请在账户授权完毕后,按 Ctrl + C 使 Docker 在后台模式下运行。" echo "请在账户授权完毕后,按 Ctrl + C 使 Docker 在后台模式下运行。"
echo "如果已开启网页登录,请直接使用 Ctrl + C 使 Docker 在后台模式下运行。"
echo echo
sleep 2
echo "Hello world!" > /pagermaid/workdir/install.lock echo "Hello world!" > /pagermaid/workdir/install.lock
sleep 2
python -m pagermaid python -m pagermaid
exit 0 exit 0
} }
main () { main () {
cd /pagermaid/workdir cd /pagermaid/workdir || exit
if [ ! -s "/pagermaid/workdir/install.lock" ]; then if [ ! -s "/pagermaid/workdir/install.lock" ]; then
welcome welcome
configure configure

View File

@ -74,11 +74,32 @@ need_web () {
esac esac
} }
need_web_login () {
PGM_WEB_LOGIN=false
case $PGM_WEB in
true)
printf "请问是否需要启用 Web 登录界面 [Y/n] "
read -r web_login <&1
case $web_login in
[yY][eE][sS] | [yY])
echo "您已确认需要启用 Web 登录界面 . . ."
PGM_WEB_LOGIN=true
;;
[nN][oO] | [nN])
;;
*)
echo "输入错误,已跳过。"
;;
esac
;;
esac
}
start_docker () { start_docker () {
echo "正在启动 Docker 容器 . . ." echo "正在启动 Docker 容器 . . ."
case $PGM_WEB in case $PGM_WEB in
true) true)
docker run -dit --restart=always --name="$container_name" --hostname="$container_name" -e WEB_ENABLE="$PGM_WEB" -e WEB_SECRET_KEY="$admin_password" -e WEB_HOST=0.0.0.0 -e WEB_PORT=3333 -p 3333:3333 teampgm/pagermaid_pyro <&1 docker run -dit --restart=always --name="$container_name" --hostname="$container_name" -e WEB_ENABLE="$PGM_WEB" -e WEB_SECRET_KEY="$admin_password" -e WEB_HOST=0.0.0.0 -e WEB_PORT=3333 -e WEB_LOGIN="$PGM_WEB_LOGIN" -p 3333:3333 teampgm/pagermaid_pyro <&1
;; ;;
*) *)
docker run -dit --restart=always --name="$container_name" --hostname="$container_name" teampgm/pagermaid_pyro <&1 docker run -dit --restart=always --name="$container_name" --hostname="$container_name" teampgm/pagermaid_pyro <&1
@ -148,6 +169,7 @@ start_installation () {
access_check access_check
build_docker build_docker
need_web need_web
need_web_login
start_docker start_docker
data_persistence data_persistence
} }
@ -216,6 +238,7 @@ reinstall_pager () {
cleanup cleanup
build_docker build_docker
need_web need_web
need_web_login
start_docker start_docker
data_persistence data_persistence
} }
@ -237,10 +260,10 @@ shon_online () {
echo " 6) Docker 重装 PagerMaid" echo " 6) Docker 重装 PagerMaid"
echo " 7) 退出脚本" echo " 7) 退出脚本"
echo echo
echo " Version2.1.0" echo " Version2.2.0"
echo echo
echo -n "请输入编号: " echo -n "请输入编号: "
read N read -r N <&1
case $N in case $N in
1) 1)
start_installation start_installation