AstraOS commited on
Commit
0345c33
Β·
verified Β·
1 Parent(s): e1bee2a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +130 -33
app.py CHANGED
@@ -463,49 +463,134 @@ def execute_command(message):
463
  # url="https://t.me/python3463_bot",
464
  # status_code=status.HTTP_302_FOUND # 302 is fine too
465
  # )
466
- CACHE_TTL = 60 * 60 # 1Β hour
467
- CACHE = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
469
- BOT_USERNAME = "python3463_bot"
470
- TME_URL = f"https://t.me/{BOT_USERNAME}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
 
472
- def get_avatar_url() -> str | None:
473
- now = time.time()
474
- if "photo" in CACHE and now - CACHE["ts"] < CACHE_TTL:
475
- return CACHE["photo"]
476
-
477
- html = requests.get(TME_URL, timeout=5).text
478
- m = re.search(r'<meta property="og:image" content="([^"]+)"', html)
479
- if m:
480
- CACHE["photo"] = m.group(1)
481
- CACHE["ts"] = now
482
- return CACHE["photo"]
483
- return None
484
-
485
- app = FastAPI()
486
-
487
- @app.get("/", include_in_schema=False)
488
- def root():
489
- photo = get_avatar_url() or "/static/telegram.png"
490
  html = f"""
491
  <html>
492
  <head>
493
- <title>Pythonβ€―Bot</title>
494
  <style>
495
- body{{font-family:sans-serif;display:flex;justify-content:center;}}
496
- .card{{max-width:420px;padding:24px;border-radius:12px;
497
- box-shadow:0 4px 12px rgba(0,0,0,.1);text-align:center;}}
498
- img{{border-radius:50%;width:120px;height:120px;object-fit:cover;}}
499
- a.btn{{background:#2AABEE;color:#fff;padding:12px 24px;border-radius:8px;
500
- text-decoration:none;font-weight:bold;display:inline-block;margin-top:12px;}}
 
 
 
 
 
 
 
 
 
 
501
  </style>
502
  </head>
503
  <body>
504
  <div class="card">
505
- <img src="{photo}" alt="Bot Avatar">
506
- <h2>PythonΒ Bot</h2>
507
- <p><strong>@{BOT_USERNAME}</strong></p>
508
- <p>The fastest way to practise PythonΒ 3.</p>
509
  <a class="btn" href="https://t.me/{BOT_USERNAME}" target="_blank">StartΒ Bot</a>
510
  </div>
511
  </body>
@@ -513,6 +598,18 @@ def root():
513
  """
514
  return HTMLResponse(html)
515
 
 
 
 
 
 
 
 
 
 
 
 
 
516
  @app.on_event("startup")
517
  def startup():
518
  # Launch the bot *after* Uvicorn has started
 
463
  # url="https://t.me/python3463_bot",
464
  # status_code=status.HTTP_302_FOUND # 302 is fine too
465
  # )
466
+ from fastapi import FastAPI, HTTPException
467
+ from fastapi.responses import HTMLResponse, FileResponse
468
+ import requests, os, time, pathlib
469
+
470
+ # ──────────────────────────────
471
+ # Config
472
+ # ──────────────────────────────
473
+ # BOT_TOKEN = os.getenv("BOT_TOKEN") # required
474
+ BOT_USERNAME = os.getenv("BOT_USERNAME", "python3463_bot")
475
+ AVATAR_TTL = int(os.getenv("AVATAR_TTL", "3600")) # seconds to keep avatar fresh
476
+ DATA_DIR = pathlib.Path("/tmp") # change if you prefer
477
+ AVATAR_PATH = DATA_DIR / "bot_avatar.jpg"
478
+ AVATAR_META = {"ts": 0} # keeps timestamp of last download
479
+
480
+ if not TOKEN:
481
+ raise RuntimeError("TOKEN environment variable not set")
482
+
483
+ app = FastAPI(title="Telegram Bot Preview")
484
+
485
+
486
+ # ──────────────────────────────
487
+ # Helper: refresh avatar if needed
488
+ # ──────────────────────────────
489
+ def refresh_avatar(force: bool = False) -> None:
490
+ """
491
+ Download the bot's avatar if it's missing or stale.
492
+ Saves it to AVATAR_PATH and updates AVATAR_META['ts'].
493
+ """
494
+ now = time.time()
495
+ if not force and AVATAR_PATH.exists() and now - AVATAR_META["ts"] < AVATAR_TTL:
496
+ return # still fresh
497
 
498
+ try:
499
+ # 1. get bot id
500
+ me = requests.get(f"https://api.telegram.org/bot{TOKEN}/getMe").json()
501
+ user_id = me["result"]["id"]
502
+
503
+ # 2. get latest profile photo
504
+ photos = requests.get(
505
+ f"https://api.telegram.org/bot{TOKEN}/getUserProfilePhotos",
506
+ params={"user_id": user_id, "limit": 1},
507
+ timeout=5,
508
+ ).json()
509
+
510
+ if photos["result"]["total_count"] == 0:
511
+ return # bot has no avatar
512
+
513
+ file_id = photos["result"]["photos"][0][-1]["file_id"] # biggest size
514
+
515
+ # 3. resolve file path
516
+ file_obj = requests.get(
517
+ f"https://api.telegram.org/bot{TOKEN}/getFile",
518
+ params={"file_id": file_id},
519
+ timeout=5,
520
+ ).json()
521
+ file_path = file_obj["result"]["file_path"]
522
+
523
+ # 4. download the actual image
524
+ file_url = f"https://api.telegram.org/file/bot{TOKEN}/{file_path}"
525
+ img_bytes = requests.get(file_url, timeout=10).content
526
+
527
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
528
+ with open(AVATAR_PATH, "wb") as f:
529
+ f.write(img_bytes)
530
+
531
+ AVATAR_META["ts"] = now
532
+ print("Avatar refreshed")
533
+ except Exception as exc:
534
+ # If something goes wrong, keep the old one
535
+ print("Avatar refresh failed:", exc)
536
+
537
+
538
+ # ──────────────────────────────
539
+ # Helper: bot description (once)
540
+ # ──────────────────────────────
541
+ def get_bot_description() -> str:
542
+ resp = requests.get(
543
+ f"https://api.telegram.org/bot{TOKEN}/getMyDescription"
544
+ ).json()
545
+ return resp["result"].get("description", "")
546
+
547
+
548
+ # ──────────────────────────────
549
+ # Startup: fetch avatar once
550
+ # ──────────────────────────────
551
+ @app.on_event("startup")
552
+ def on_startup():
553
+ refresh_avatar(force=True)
554
+
555
+
556
+ # ──────────────────────────────
557
+ # Route: preview card
558
+ # ──────────────────────────────
559
+ @app.get("/", include_in_schema=False, response_class=HTMLResponse)
560
+ def preview():
561
+ refresh_avatar() # refresh if stale
562
+
563
+ avatar_src = "/avatar.jpg" if AVATAR_PATH.exists() else "https://telegram.org/img/t_logo.png"
564
+ description = get_bot_description()
565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
  html = f"""
567
  <html>
568
  <head>
569
+ <title>{BOT_USERNAME}</title>
570
  <style>
571
+ body {{
572
+ font-family: sans-serif; display: flex; justify-content: center;
573
+ padding: 40px; background: #f7f7f7;
574
+ }}
575
+ .card {{
576
+ max-width: 420px; background: #fff; padding: 24px; text-align: center;
577
+ border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,.1);
578
+ }}
579
+ .avatar {{
580
+ width: 120px; height: 120px; border-radius: 50%; object-fit: cover;
581
+ }}
582
+ .btn {{
583
+ display: inline-block; margin-top: 16px; padding: 12px 24px;
584
+ background: #2AABEE; color: #fff; border-radius: 8px;
585
+ text-decoration: none; font-weight: bold;
586
+ }}
587
  </style>
588
  </head>
589
  <body>
590
  <div class="card">
591
+ <img src="{avatar_src}" alt="avatar" class="avatar">
592
+ <h2>@{BOT_USERNAME}</h2>
593
+ <p>{description}</p>
 
594
  <a class="btn" href="https://t.me/{BOT_USERNAME}" target="_blank">StartΒ Bot</a>
595
  </div>
596
  </body>
 
598
  """
599
  return HTMLResponse(html)
600
 
601
+
602
+ # ──────────────────────────────
603
+ # Route: serve avatar file
604
+ # ──────────────────────────────
605
+ @app.get("/avatar.jpg", include_in_schema=False)
606
+ def avatar():
607
+ refresh_avatar() # make sure it's fresh
608
+ if not AVATAR_PATH.exists():
609
+ raise HTTPException(status_code=404, detail="Avatar not found")
610
+ return FileResponse(AVATAR_PATH, media_type="image/jpeg")
611
+
612
+
613
  @app.on_event("startup")
614
  def startup():
615
  # Launch the bot *after* Uvicorn has started