AstraOS commited on
Commit
8dd998f
Β·
verified Β·
1 Parent(s): c31350f

Create telegram_preview.py

Browse files
Files changed (1) hide show
  1. telegram_preview.py +149 -0
telegram_preview.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Reusable Telegram‑bot preview router.
3
+
4
+ Exposes:
5
+ β€’ GET / – HTML preview card
6
+ β€’ GET /avatar.jpg – bot avatar (streamed, in‑memory)
7
+
8
+ To use: `from telegram_preview import router` and include it in your FastAPI app.
9
+ Compatible with PythonΒ 3.8+ (uses typing.Optional, no PEPβ€―604 syntax).
10
+ """
11
+
12
+ import io
13
+ import os
14
+ import requests
15
+ from typing import Optional
16
+
17
+ from fastapi import APIRouter, Response
18
+ from fastapi.responses import HTMLResponse, StreamingResponse
19
+
20
+ # ─── Environment ───────────────────────────────────────────────────────────────
21
+ TOKEN = os.getenv("BOT_TOKEN") # required
22
+ BOT_USERNAME = os.getenv("BOT_USERNAME", "python3463_bot")
23
+ FALLBACK_IMG = "https://telegram.org/img/t_logo.png" # fallback logo URL
24
+
25
+ if not TOKEN:
26
+ raise RuntimeError("TOKEN environment variable not set")
27
+
28
+ API_ROOT = f"https://api.telegram.org/bot{TOKEN}"
29
+
30
+ router = APIRouter()
31
+
32
+
33
+ # ─── Helpers ───────────────────────────────────────────────────────────────────
34
+ def _get_description() -> str:
35
+ """Try short β†’ full β†’ empty string."""
36
+ try:
37
+ r = requests.get(f"{API_ROOT}/getMyShortDescription", timeout=5).json()
38
+ if (desc := r["result"].get("short_description")):
39
+ return desc
40
+ except Exception:
41
+ pass
42
+
43
+ try:
44
+ r = requests.get(f"{API_ROOT}/getMyDescription", timeout=5).json()
45
+ return r["result"].get("description", "")
46
+ except Exception:
47
+ return ""
48
+
49
+
50
+ def _fetch_avatar_bytes() -> Optional[bytes]:
51
+ """Return the newest avatar as bytes, or None if missing/error."""
52
+ try:
53
+ me = requests.get(f"{API_ROOT}/getMe", timeout=5).json()
54
+ user_id = me["result"]["id"]
55
+
56
+ photos = requests.get(
57
+ f"{API_ROOT}/getUserProfilePhotos",
58
+ params={"user_id": user_id, "limit": 1},
59
+ timeout=5,
60
+ ).json()
61
+
62
+ if photos["result"]["total_count"] == 0:
63
+ return None
64
+
65
+ file_id = photos["result"]["photos"][0][-1]["file_id"]
66
+ file_obj = requests.get(
67
+ f"{API_ROOT}/getFile", params={"file_id": file_id}, timeout=5
68
+ ).json()
69
+ file_path = file_obj["result"]["file_path"]
70
+
71
+ resp = requests.get(
72
+ f"https://api.telegram.org/file/bot{TOKEN}/{file_path}", timeout=10
73
+ )
74
+ if resp.status_code == 200:
75
+ return resp.content
76
+ except Exception as exc:
77
+ print("Avatar fetch error:", exc)
78
+
79
+ return None
80
+
81
+
82
+ # ─── Routes ────────────────────────────────────────────────────────────────────
83
+ @router.get("/", include_in_schema=False, response_class=HTMLResponse)
84
+ def bot_preview():
85
+ description = _get_description()
86
+
87
+ html = f"""
88
+ <html>
89
+ <head>
90
+ <title>@{BOT_USERNAME}</title>
91
+ <style>
92
+ body {{
93
+ font-family: sans-serif;
94
+ display: flex;
95
+ justify-content: center;
96
+ padding: 40px;
97
+ background: #f7f7f7;
98
+ }}
99
+ .card {{
100
+ max-width: 420px;
101
+ background: #fff;
102
+ padding: 24px;
103
+ text-align: center;
104
+ border-radius: 12px;
105
+ box-shadow: 0 4px 12px rgba(0,0,0,.1);
106
+ }}
107
+ .avatar {{
108
+ width: 120px;
109
+ height: 120px;
110
+ border-radius: 50%;
111
+ object-fit: cover;
112
+ background: #ddd;
113
+ }}
114
+ .btn {{
115
+ display: inline-block;
116
+ margin-top: 16px;
117
+ padding: 12px 24px;
118
+ background: #2AABEE;
119
+ color: #fff;
120
+ border-radius: 8px;
121
+ text-decoration: none;
122
+ font-weight: bold;
123
+ }}
124
+ </style>
125
+ </head>
126
+ <body>
127
+ <div class="card">
128
+ <img src="avatar.jpg" class="avatar" alt="avatar">
129
+ <h2>@{BOT_USERNAME}</h2>
130
+ <p>{description}</p>
131
+ <a class="btn" href="https://t.me/{BOT_USERNAME}" target="_blank">
132
+ StartΒ Bot
133
+ </a>
134
+ </div>
135
+ </body>
136
+ </html>
137
+ """
138
+ return HTMLResponse(html)
139
+
140
+
141
+ @router.get("/avatar.jpg", include_in_schema=False)
142
+ def serve_avatar():
143
+ """Streams avatar JPEG or Telegram logo (PNG) as fallback."""
144
+ data = _fetch_avatar_bytes()
145
+ if data:
146
+ return StreamingResponse(io.BytesIO(data), media_type="image/jpeg")
147
+
148
+ fallback = requests.get(FALLBACK_IMG, timeout=10)
149
+ return Response(content=fallback.content, media_type="image/png")