126 lines
4.2 KiB
Python
126 lines
4.2 KiB
Python
"""Разовая активация MAX-сессии (запускать на своём ПК, не на сервере):
|
|
|
|
python -m app.login --phone +79991234567
|
|
(попросит код из SMS/пуша MAX, можно через --code-file для автоматики)
|
|
python -m app.login --chats # список чатов с chatId (для MAX_TARGET_CHAT)
|
|
|
|
Результат: max_session.json (device_id, login_token, my_id) — значения едут в
|
|
переменные Coolify: MAX_DEVICE_ID, MAX_LOGIN_TOKEN, MAX_TARGET_CHAT."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from vkmax.client import MaxClient
|
|
from vkmax.functions.messages import send_message
|
|
|
|
SESSION_FILE = Path(
|
|
os.environ.get("MAX_SESSION_FILE")
|
|
or Path(__file__).resolve().parent.parent / "max_session.json"
|
|
)
|
|
|
|
|
|
def _load() -> dict:
|
|
return json.loads(SESSION_FILE.read_text("utf-8"))
|
|
|
|
|
|
def _find_keys(node, wanted: set[str], out: dict | None = None) -> dict:
|
|
if out is None:
|
|
out = {}
|
|
if isinstance(node, dict):
|
|
for k, v in node.items():
|
|
if k in wanted and isinstance(v, (int, str)) and k not in out:
|
|
out[k] = v
|
|
_find_keys(v, wanted, out)
|
|
elif isinstance(node, list):
|
|
for v in node[:60]:
|
|
_find_keys(v, wanted, out)
|
|
return out
|
|
|
|
|
|
async def _wait_code(code_file: str | None) -> str:
|
|
if code_file:
|
|
p = Path(code_file)
|
|
print(f"RELAY-AWAIT-CODE: запишите код в {p.resolve()}", flush=True)
|
|
while not p.exists():
|
|
await asyncio.sleep(2)
|
|
await asyncio.sleep(1)
|
|
code = p.read_text(errors="replace").strip()
|
|
p.unlink(missing_ok=True)
|
|
return code
|
|
return input("Код подтверждения из MAX: ").strip()
|
|
|
|
|
|
async def login(phone: str, code_file: str | None) -> None:
|
|
client = MaxClient()
|
|
await client.connect()
|
|
sms_token = await client.send_code(phone)
|
|
code = await _wait_code(code_file)
|
|
resp = await client.sign_in(sms_token, int(code))
|
|
payload = resp["payload"]
|
|
login_token = payload["tokenAttrs"]["LOGIN"]["token"]
|
|
ids = _find_keys(payload, {"myId", "userId"})
|
|
session = {
|
|
"device_id": client.device_id,
|
|
"login_token": login_token,
|
|
"my_id": ids.get("myId") or ids.get("userId"),
|
|
}
|
|
SESSION_FILE.write_text(json.dumps(session, indent=2), "utf-8")
|
|
await client.disconnect()
|
|
print("RELAY-SESSION-DONE", flush=True)
|
|
print(json.dumps(session, ensure_ascii=False, indent=1))
|
|
|
|
|
|
async def chats() -> None:
|
|
s = _load()
|
|
client = MaxClient()
|
|
await client.connect()
|
|
resp = await client.login_by_token(s["login_token"], s["device_id"])
|
|
payload = resp["payload"]
|
|
print("payload keys:", sorted(payload.keys()))
|
|
chat_list = payload.get("chats")
|
|
if isinstance(chat_list, dict):
|
|
chat_list = chat_list.get("chats") or list(chat_list.values())
|
|
if isinstance(chat_list, list):
|
|
for c in chat_list[:60]:
|
|
cid = c.get("chatId") or c.get("id")
|
|
info = c.get("chat") or c.get("lastChat") or c
|
|
print(f"- chatId={cid} | title={info.get('title')!r} | type={info.get('type')}")
|
|
else:
|
|
print(json.dumps(payload, ensure_ascii=False)[:4000])
|
|
await client.disconnect()
|
|
|
|
|
|
async def test(chat_id: int) -> None:
|
|
s = _load()
|
|
client = MaxClient()
|
|
await client.connect()
|
|
await client.login_by_token(s["login_token"], s["device_id"])
|
|
await send_message(client, chat_id, "🟢 tg2max-relay: проверка сессии (локальный запуск)")
|
|
print("TEST-SENT to", chat_id, flush=True)
|
|
await client.disconnect()
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--phone")
|
|
ap.add_argument("--code-file")
|
|
ap.add_argument("--chats", action="store_true")
|
|
ap.add_argument("--test", type=int, metavar="CHAT_ID")
|
|
a = ap.parse_args()
|
|
if a.phone:
|
|
asyncio.run(login(a.phone, a.code_file))
|
|
elif a.chats:
|
|
asyncio.run(chats())
|
|
elif a.test:
|
|
asyncio.run(test(a.test))
|
|
else:
|
|
ap.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|