iozxv commited on
Commit
bde84b9
·
verified ·
1 Parent(s): 1515eb8

Upload 6 files

Browse files
Files changed (3) hide show
  1. README.md +50 -5
  2. database.py +5 -1
  3. main.py +15 -233
README.md CHANGED
@@ -1,6 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
- license: apache-2.0
3
- title: C1
4
- sdk: docker
5
- emoji: 🏆
6
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Space Pinger Bot 🚀
2
+
3
+ A Telegram bot built with **Pyrogram** and **FastAPI** to keep Hugging Face spaces alive by pinging them sequentially every 5 minutes. The database containing authorized users, admins, and space URLs is automatically created and synced to a Hugging Face dataset for persistent storage.
4
+
5
+ ## 🛠️ Required Secret Environment Variables
6
+
7
+ When deploying this bot on Hugging Face Spaces (or any Docker platform), you **MUST** configure the following variables in the Space settings (under **Variables and Secrets**):
8
+
9
+ 1. **`API_ID`**: The API ID for your Telegram application (get it from [my.telegram.org](https://my.telegram.org)).
10
+ 2. **`API_HASH`**: The API Hash for your Telegram application (get it from [my.telegram.org](https://my.telegram.org)).
11
+ 3. **`BOT_TOKEN`**: The Telegram Bot Token generated by [@BotFather](https://t.me/BotFather).
12
+ 4. **`ADMIN_ID`**: The numeric Telegram ID of the **Super Admin** (e.g., `123456789`). The Super Admin has highest authority (e.g., can remove admins).
13
+ 5. **`HF_TOKEN`**: A Hugging Face User Access Token with **WRITE** permissions (generate it under your Hugging Face account settings at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)).
14
+ 6. **`HF_DATASET_URL`**: The full URL of your Hugging Face dataset repository (e.g. `https://huggingface.co/datasets/your_username/your_dataset_name` or simply the repository ID like `your_username/your_dataset_name`). The bot will automatically create this dataset if it doesn't already exist and maintain `database.json` within it.
15
+
16
  ---
17
+
18
+ ## 🤖 Available Commands
19
+
20
+ ### 💬 Public Commands (Anyone)
21
+ - `/id` - View your Telegram User ID (used to request authorization).
22
+ - `/start` - Displays a simple welcome message.
23
+ - `/help` - Explains what the bot does.
24
+ - `/commands` - List commands matching the caller's authorization level.
25
+
26
+ ### 👤 Authorized User Commands
27
+ - `/add <hf_url>` - Register a Hugging Face Space URL. (You can also just send a raw Hugging Face URL directly, and the bot will normalize and register it automatically).
28
+ - `/remove <hf_url>` - Remove a Space URL you registered.
29
+ - `/list_url` - List the Space URLs that *you* registered.
30
+
31
+ ### 👮 Admin Commands
32
+ - `/add_user <user_id>` - Authorize a user to register and manage their own URLs.
33
+ - `/remove_user <user_id>` - Deauthorize a user.
34
+ - `/add_admin <user_id>` - Promote a user to Admin.
35
+ - `/list_url` - List **ALL** registered Space URLs in the system along with who registered them.
36
+ - `/list` - List all Admins and Users (excluding the Super Admin).
37
+
38
+ ### 👑 Super Admin Commands
39
+ - `/remove_admin <user_id>` - Demote an Admin back to a regular user.
40
+ - `/list` - List the Super Admin, all Admins, and all Users.
41
+
42
+ ---
43
+
44
+ ## 🐳 Deployment to Hugging Face Spaces
45
+
46
+ 1. Create a new Space on Hugging Face: [huggingface.co/new-space](https://huggingface.co/new-space).
47
+ 2. Choose **Docker** as the SDK.
48
+ 3. Select **Blank** (or any Docker template).
49
+ 4. Go to the Space **Settings** page and add the **Secrets** listed above.
50
+ 5. Push the repository files (`Dockerfile`, `requirements.txt`, `config.py`, `database.py`, `main.py`) to the Space.
51
+ 6. The space will automatically build the Docker image, run the FastAPI web server on port `7860`, launch the Telegram bot, and begin the sequential 5-minute pinger loop.
database.py CHANGED
@@ -83,7 +83,11 @@ def init_db():
83
 
84
  print("Successfully synced database from Hugging Face.")
85
  except Exception as e:
86
- print(f"Database not found on HF or error loading: {e}. Initializing a new one.")
 
 
 
 
87
  if os.path.exists(LOCAL_PATH):
88
  try:
89
  with open(LOCAL_PATH, "r", encoding="utf-8") as f:
 
83
 
84
  print("Successfully synced database from Hugging Face.")
85
  except Exception as e:
86
+ err_msg = str(e)
87
+ if "404" in err_msg or "Entry Not Found" in err_msg:
88
+ print("Database file not found in HF dataset. Initializing a new one.")
89
+ else:
90
+ print(f"Error loading database from HF: {err_msg}. Initializing a new one.")
91
  if os.path.exists(LOCAL_PATH):
92
  try:
93
  with open(LOCAL_PATH, "r", encoding="utf-8") as f:
main.py CHANGED
@@ -2,7 +2,6 @@ import asyncio
2
  import os
3
  import httpx
4
  from fastapi import FastAPI
5
- from fastapi.responses import HTMLResponse
6
  from contextlib import asynccontextmanager
7
  from pyrogram import Client, filters
8
  import config
@@ -126,238 +125,10 @@ async def lifespan(app: FastAPI):
126
 
127
  app = FastAPI(lifespan=lifespan)
128
 
129
- # Web Dashboard
130
- @app.get("/", response_class=HTMLResponse)
131
- async def dashboard():
132
- urls = database.get_all_urls()
133
- own_url = get_own_space_url() or "Not detected (running locally?)"
134
-
135
- urls_list_html = "".join([f"<li><code>{url}</code></li>" for url in urls])
136
- if not urls:
137
- urls_list_html = "<li>No external spaces added yet.</li>"
138
-
139
- config_status = {
140
- "Telegram Bot Token": config.BOT_TOKEN is not None,
141
- "Super Admin ID": config.SUPER_ADMIN_ID is not None,
142
- "HF Access Token": config.HF_TOKEN is not None,
143
- "HF Dataset URL": config.HF_DATASET_URL is not None,
144
- }
145
-
146
- config_rows = ""
147
- for name, ok in config_status.items():
148
- status_badge = '<span class="badge ok">Configured</span>' if ok else '<span class="badge missing">Missing</span>'
149
- config_rows += f"<tr><td>{name}</td><td>{status_badge}</td></tr>"
150
-
151
- html_content = f"""
152
- <!DOCTYPE html>
153
- <html lang="en">
154
- <head>
155
- <meta charset="UTF-8">
156
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
157
- <title>Hugging Face Space Pinger Dashboard</title>
158
- <link rel="preconnect" href="https://fonts.googleapis.com">
159
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
160
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Space+Grotesk:wght@400;600&display=swap" rel="stylesheet">
161
- <style>
162
- :root {{
163
- --bg: #0b0f19;
164
- --card-bg: rgba(255, 255, 255, 0.03);
165
- --card-border: rgba(255, 255, 255, 0.08);
166
- --primary: #4f46e5;
167
- --primary-glow: rgba(79, 70, 229, 0.4);
168
- --success: #10b981;
169
- --success-glow: rgba(16, 185, 129, 0.4);
170
- --text-main: #f3f4f6;
171
- --text-muted: #9ca3af;
172
- }}
173
- * {{
174
- box-sizing: border-box;
175
- margin: 0;
176
- padding: 0;
177
- }}
178
- body {{
179
- font-family: 'Outfit', sans-serif;
180
- background-color: var(--bg);
181
- color: var(--text-main);
182
- min-height: 100vh;
183
- display: flex;
184
- flex-direction: column;
185
- justify-content: center;
186
- align-items: center;
187
- padding: 2rem;
188
- background-image:
189
- radial-gradient(circle at 10% 20%, rgba(79, 70, 229, 0.08) 0%, transparent 40%),
190
- radial-gradient(circle at 90% 80%, rgba(16, 185, 129, 0.06) 0%, transparent 40%);
191
- }}
192
- .container {{
193
- max-width: 800px;
194
- width: 100%;
195
- display: flex;
196
- flex-direction: column;
197
- gap: 2rem;
198
- }}
199
- header {{
200
- text-align: center;
201
- margin-bottom: 1rem;
202
- }}
203
- h1 {{
204
- font-family: 'Space Grotesk', sans-serif;
205
- font-weight: 800;
206
- font-size: 2.5rem;
207
- background: linear-gradient(135deg, #a5b4fc, #818cf8, #6366f1);
208
- -webkit-background-clip: text;
209
- -webkit-text-fill-color: transparent;
210
- margin-bottom: 0.5rem;
211
- }}
212
- .subtitle {{
213
- color: var(--text-muted);
214
- font-size: 1.1rem;
215
- }}
216
- .grid {{
217
- display: grid;
218
- grid-template-columns: 1fr 1fr;
219
- gap: 1.5rem;
220
- }}
221
- @media (max-width: 600px) {{
222
- .grid {{
223
- grid-template-columns: 1fr;
224
- }}
225
- }}
226
- .card {{
227
- background: var(--card-bg);
228
- border: 1px solid var(--card-border);
229
- border-radius: 16px;
230
- padding: 1.5rem;
231
- backdrop-filter: blur(12px);
232
- box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
233
- transition: transform 0.3s ease, border-color 0.3s ease;
234
- }}
235
- .card:hover {{
236
- transform: translateY(-5px);
237
- border-color: rgba(99, 102, 241, 0.2);
238
- }}
239
- .card-title {{
240
- font-family: 'Space Grotesk', sans-serif;
241
- font-size: 1.25rem;
242
- font-weight: 600;
243
- margin-bottom: 1rem;
244
- display: flex;
245
- align-items: center;
246
- gap: 0.5rem;
247
- color: #a5b4fc;
248
- }}
249
- .status-badge {{
250
- display: inline-flex;
251
- align-items: center;
252
- gap: 0.5rem;
253
- padding: 0.5rem 1rem;
254
- background: rgba(16, 185, 129, 0.1);
255
- border: 1px solid var(--success);
256
- color: var(--success);
257
- border-radius: 9999px;
258
- font-weight: 600;
259
- box-shadow: 0 0 15px var(--success-glow);
260
- animation: pulse 2s infinite;
261
- }}
262
- @keyframes pulse {{
263
- 0% {{ box-shadow: 0 0 5px var(--success-glow); }}
264
- 50% {{ box-shadow: 0 0 20px var(--success-glow); }}
265
- 100% {{ box-shadow: 0 0 5px var(--success-glow); }}
266
- }}
267
- ul {{
268
- list-style: none;
269
- display: flex;
270
- flex-direction: column;
271
- gap: 0.75rem;
272
- }}
273
- li {{
274
- background: rgba(255, 255, 255, 0.02);
275
- border: 1px solid rgba(255, 255, 255, 0.05);
276
- border-radius: 8px;
277
- padding: 0.75rem;
278
- font-size: 0.9rem;
279
- overflow-x: auto;
280
- }}
281
- code {{
282
- font-family: 'Courier New', Courier, monospace;
283
- color: #818cf8;
284
- }}
285
- table {{
286
- width: 100%;
287
- border-collapse: collapse;
288
- }}
289
- td {{
290
- padding: 0.75rem 0;
291
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
292
- }}
293
- tr:last-child td {{
294
- border-bottom: none;
295
- }}
296
- .badge {{
297
- display: inline-block;
298
- padding: 0.25rem 0.5rem;
299
- border-radius: 4px;
300
- font-size: 0.8rem;
301
- font-weight: 600;
302
- }}
303
- .badge.ok {{
304
- background: rgba(16, 185, 129, 0.15);
305
- color: var(--success);
306
- }}
307
- .badge.missing {{
308
- background: rgba(239, 68, 68, 0.15);
309
- color: #ef4444;
310
- }}
311
- footer {{
312
- text-align: center;
313
- color: var(--text-muted);
314
- font-size: 0.85rem;
315
- margin-top: 2rem;
316
- }}
317
- </style>
318
- </head>
319
- <body>
320
- <div class="container">
321
- <header>
322
- <h1>HF Space Pinger</h1>
323
- <p class="subtitle">Reliable Hugging Face Space keep-alive bot</p>
324
- </header>
325
-
326
- <div class="card" style="text-align: center; display: flex; flex-direction: column; align-items: center; gap: 1rem;">
327
- <div class="card-title">SYSTEM STATUS</div>
328
- <div>
329
- <span class="status-badge">
330
- <span style="display:inline-block; width:8px; height:8px; background:var(--success); border-radius:50%;"></span>
331
- BOT IS ACTIVE
332
- </span>
333
- </div>
334
- <p style="color: var(--text-muted); margin-top: 0.5rem;">Own Space URL: <code>{own_url}</code></p>
335
- </div>
336
-
337
- <div class="grid">
338
- <div class="card">
339
- <div class="card-title">📋 Config Settings</div>
340
- <table>
341
- {config_rows}
342
- </table>
343
- </div>
344
-
345
- <div class="card">
346
- <div class="card-title">🔗 Monitored Spaces ({len(urls)})</div>
347
- <ul>
348
- {urls_list_html}
349
- </ul>
350
- </div>
351
- </div>
352
-
353
- <footer>
354
- Powered by FastAPI, Pyrogram & Hugging Face • Kept awake automatically
355
- </footer>
356
- </div>
357
- </body>
358
- </html>
359
- """
360
- return html_content
361
 
362
  # ================== PYROGRAM BOT HANDLERS ==================
363
 
@@ -366,6 +137,17 @@ async def start_command(client, message):
366
  if not message.from_user:
367
  return
368
  log_and_update(message)
 
 
 
 
 
 
 
 
 
 
 
369
  # Welcomes the user
370
  await message.reply_text(
371
  "👋 **Hello! Welcome to the Hugging Face Space Pinger Bot.** 🚀\n\n"
 
2
  import os
3
  import httpx
4
  from fastapi import FastAPI
 
5
  from contextlib import asynccontextmanager
6
  from pyrogram import Client, filters
7
  import config
 
125
 
126
  app = FastAPI(lifespan=lifespan)
127
 
128
+ # Health Check Endpoint
129
+ @app.get("/")
130
+ async def health_check():
131
+ return {"status": "ok", "message": "HF Space Pinger Bot is running successfully."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
  # ================== PYROGRAM BOT HANDLERS ==================
134
 
 
137
  if not message.from_user:
138
  return
139
  log_and_update(message)
140
+
141
+ user_id = message.from_user.id
142
+ if not database.is_authorized(user_id):
143
+ await message.reply_text(
144
+ "❌ **Unauthorized User**\n\n"
145
+ f"You are unauthorized to use the bot.\n"
146
+ f"Your ID: `{user_id}`\n\n"
147
+ "Tell an admin to add you."
148
+ )
149
+ return
150
+
151
  # Welcomes the user
152
  await message.reply_text(
153
  "👋 **Hello! Welcome to the Hugging Face Space Pinger Bot.** 🚀\n\n"