Spaces:
Running
Running
File size: 4,921 Bytes
58f00c9 a00abaf 58f00c9 5f400f8 58f00c9 5f400f8 58f00c9 5f400f8 58f00c9 84e63a1 58f00c9 529b973 a00abaf 529b973 a00abaf 58f00c9 a00abaf 58f00c9 84e63a1 58f00c9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | """Entry point: ``python -m bot``."""
from __future__ import annotations
import asyncio
import logging
import shutil
import signal
import sys
from pathlib import Path
from typing import Awaitable, Callable
from . import __version__, errorlog, sysinfo
from .config import Config, ConfigError, load_config
from .db import Database
log = logging.getLogger("bot")
TEMP_PREFIXES = ("job", "tracks", "ethumb", "minfo") # the folders the bot makes in TEMP_DIR
def clean_temp_dir(path: str) -> None:
"""Remove leftovers of a crashed run; only touches our own job folders."""
root = Path(path)
root.mkdir(parents=True, exist_ok=True)
for child in root.iterdir():
if child.is_dir() and child.name.startswith(TEMP_PREFIXES):
shutil.rmtree(child, ignore_errors=True)
async def build_client(cfg: Config, db: Database):
from telethon import TelegramClient
from telethon.sessions import StringSession
token_id = cfg.bot_token.split(":")[0]
session = cfg.session_string
if not session and await db.kv_get("session_bot") == token_id:
session = await db.kv_get("session") or "" # keeps the login across Space restarts
client = TelegramClient(
StringSession(session or None), cfg.api_id, cfg.api_hash,
connection_retries=-1, retry_delay=2, auto_reconnect=True, flood_sleep_threshold=60,
)
await client.start(bot_token=cfg.bot_token)
await db.kv_set("session", client.session.save())
await db.kv_set("session_bot", token_id)
return client
async def amain() -> int:
from .app import App
from .web import start_web
cfg = load_config()
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
logging.getLogger("telethon").setLevel(logging.WARNING)
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
log.info("Auto Rename Bot v%s starting", __version__)
clean_temp_dir(cfg.temp_dir)
db = Database(cfg.database_url)
await db.init()
if db.url.startswith("sqlite"):
log.warning("DATABASE_URL is not set: using local SQLite, so settings are lost when the Space restarts")
runner = await start_web(cfg.port) # up first: the Space must answer its health check while we log in
client = await build_client(cfg, db)
me = await client.get_me()
app = App(client, cfg, db)
username = getattr(me, "username", "") or ""
app.telegraph.set_author(getattr(me, "first_name", "") or username, f"https://t.me/{username}" if username else "")
await app.setup()
channel_log = errorlog.install(app._log_send, errorlog.secrets_from(cfg)) if cfg.log_channel else None # errors -> LOG_CHANNEL
log.info("@%s is online (DC %s, %d connection(s) per transfer, fast transfer %s)",
me.username, client.session.dc_id, cfg.connections, "on" if cfg.fast_transfer else "off")
speed = sysinfo.speed_summary(cfg.fast_transfer, cfg.connections, cfg.stream_transfer)
log.info("speed: %s", speed)
for _short, detail, fine in sysinfo.speed_checks(cfg.fast_transfer, cfg.connections, cfg.stream_transfer):
if not fine:
log.warning("speed: %s", detail)
if cfg.log_channel:
try:
await app._log_send(f"✅ <b>Bot started</b> · v{__version__} · DC {client.session.dc_id}\n⚡ {speed}")
except Exception as exc: # noqa: BLE001
log.warning("could not post to LOG_CHANNEL: %s", exc)
stop = asyncio.Event()
loop = asyncio.get_running_loop()
for sig in (signal.SIGINT, signal.SIGTERM):
try:
loop.add_signal_handler(sig, stop.set)
except NotImplementedError: # Windows
pass
disconnected = asyncio.ensure_future(client.run_until_disconnected())
stopper = asyncio.ensure_future(stop.wait())
await asyncio.wait({disconnected, stopper}, return_when=asyncio.FIRST_COMPLETED)
crashed = disconnected.done() and not stop.is_set()
log.info("shutting down")
await app.shutdown()
if channel_log is not None:
await channel_log.aclose() # sends the reports still on their way, while the client is connected
await runner.cleanup()
try:
await client.disconnect()
except Exception: # noqa: BLE001
pass
await db.close()
return 1 if crashed else 0
def _run(main: Callable[[], Awaitable[int]]) -> int:
try:
import uvloop
except ImportError:
return asyncio.run(main())
if sys.version_info >= (3, 12):
return asyncio.run(main(), loop_factory=uvloop.new_event_loop)
uvloop.install()
return asyncio.run(main())
def main() -> None:
try:
code = _run(amain)
except ConfigError as exc:
print(f"Configuration error: {exc}", file=sys.stderr)
sys.exit(2)
except KeyboardInterrupt:
code = 0
sys.exit(code)
if __name__ == "__main__":
main()
|