Spaces:
Sleeping
Sleeping
Update bot.py
Browse files
bot.py
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
import asyncio
|
2 |
import logging
|
3 |
import os
|
@@ -7,7 +9,7 @@ import time
|
|
7 |
from typing import Dict
|
8 |
|
9 |
from aiogram import Bot, Dispatcher, types, F
|
10 |
-
from aiogram.filters import CommandStart
|
11 |
from aiogram.types import Message, FSInputFile
|
12 |
from aiogram.enums import ParseMode
|
13 |
from aiogram.exceptions import TelegramBadRequest
|
@@ -42,7 +44,6 @@ bot = Bot(
|
|
42 |
dp = Dispatcher()
|
43 |
TASK_QUEUE = asyncio.Queue()
|
44 |
BATCH_JOBS: Dict[str, Dict] = {}
|
45 |
-
BOT_START_TIME = time.time()
|
46 |
|
47 |
# --- Worker Logic ---
|
48 |
async def link_processor_worker(worker_id: int):
|
@@ -73,10 +74,7 @@ async def link_processor_worker(worker_id: int):
|
|
73 |
if error:
|
74 |
raise ValueError(error)
|
75 |
|
76 |
-
local_filepath,
|
77 |
-
download_url,
|
78 |
-
raw_filename
|
79 |
-
)
|
80 |
if download_error:
|
81 |
raise ValueError(download_error)
|
82 |
|
@@ -85,8 +83,7 @@ async def link_processor_worker(worker_id: int):
|
|
85 |
'path': local_filepath,
|
86 |
'name': raw_filename,
|
87 |
'size': os.path.getsize(local_filepath),
|
88 |
-
'short_id': short_id
|
89 |
-
'thumb': thumb_path
|
90 |
}
|
91 |
|
92 |
except Exception as e:
|
@@ -100,12 +97,18 @@ async def link_processor_worker(worker_id: int):
|
|
100 |
batch_info["failed_links"].append({"link": original_link, "error": error_msg})
|
101 |
|
102 |
processed, total = batch_info['processed_links'], batch_info['total_links']
|
103 |
-
|
|
|
104 |
|
105 |
try:
|
106 |
-
await
|
107 |
-
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
109 |
)
|
110 |
except TelegramBadRequest:
|
111 |
pass
|
@@ -116,29 +119,17 @@ async def link_processor_worker(worker_id: int):
|
|
116 |
|
117 |
TASK_QUEUE.task_done()
|
118 |
|
119 |
-
async def send_and_cache_file(chat_id: int, file_info: dict, caption: str
|
120 |
file_path_or_id = file_info.get('file_id') or FSInputFile(file_info['path'], filename=file_info['name'])
|
121 |
media_type = file_info.get('type')
|
122 |
filename = file_info.get('name') or file_info.get('filename')
|
123 |
|
124 |
-
|
125 |
-
audio_exts = ('.mp3', '.flac', '.ogg', '.wav')
|
126 |
-
|
127 |
-
sent_message = None
|
128 |
-
if media_type == 'video' or filename.lower().endswith(video_exts):
|
129 |
-
sent_message = await bot.send_video(chat_id, file_path_or_id, caption=caption, supports_streaming=True, reply_to_message_id=reply_to)
|
130 |
-
media_type = 'video'
|
131 |
-
elif media_type == 'audio' or filename.lower().endswith(audio_exts):
|
132 |
-
sent_message = await bot.send_audio(chat_id, file_path_or_id, caption=caption, reply_to_message_id=reply_to)
|
133 |
-
media_type = 'audio'
|
134 |
-
else:
|
135 |
-
sent_message = await bot.send_document(chat_id, file_path_or_id, caption=caption, reply_to_message_id=reply_to)
|
136 |
-
media_type = 'document'
|
137 |
|
138 |
if not file_info.get('cached') and sent_message:
|
139 |
-
file_id_to_cache =
|
140 |
await db_utils.add_to_cache(
|
141 |
-
file_info['short_id'], file_id_to_cache, filename,
|
142 |
)
|
143 |
|
144 |
return sent_message
|
@@ -148,23 +139,32 @@ async def handle_batch_completion(batch_id: str):
|
|
148 |
if not batch:
|
149 |
return
|
150 |
|
151 |
-
status_msg = batch["status_message"]
|
152 |
successful_downloads = batch["successful_downloads"]
|
153 |
|
154 |
try:
|
155 |
if not successful_downloads:
|
156 |
failed_links_text = "\n".join(
|
157 |
-
[f"- {x['link']}
|
158 |
)
|
159 |
-
await
|
160 |
-
|
|
|
|
|
|
|
|
|
|
|
161 |
)
|
162 |
return
|
163 |
|
164 |
-
await
|
165 |
-
|
|
|
|
|
|
|
|
|
166 |
)
|
167 |
|
|
|
168 |
if config.FORWARD_CHANNEL_ID:
|
169 |
await bot.forward_message(
|
170 |
config.FORWARD_CHANNEL_ID,
|
@@ -173,30 +173,36 @@ async def handle_batch_completion(batch_id: str):
|
|
173 |
)
|
174 |
for item in successful_downloads:
|
175 |
caption = f"`{item.get('name') or item.get('filename')}`"
|
176 |
-
await send_and_cache_file(config.FORWARD_CHANNEL_ID, item, caption
|
177 |
await asyncio.sleep(1)
|
178 |
|
179 |
for item in successful_downloads:
|
180 |
caption = f"`{item.get('name') or item.get('filename')}`"
|
181 |
-
await send_and_cache_file(batch["source_chat_id"], item, caption
|
182 |
|
183 |
-
summary = f"β
Batch
|
184 |
if batch["failed_links"]:
|
185 |
summary += f"\nβ {len(batch['failed_links'])} links failed."
|
186 |
|
187 |
-
await
|
|
|
|
|
|
|
|
|
188 |
|
189 |
except Exception as e:
|
190 |
logger.error(f"Error during batch completion for {batch_id}: {e}", exc_info=True)
|
191 |
-
await
|
192 |
-
|
|
|
|
|
|
|
|
|
193 |
)
|
194 |
finally:
|
195 |
for item in successful_downloads:
|
196 |
if not item.get('cached') and os.path.exists(item['path']):
|
197 |
os.remove(item['path'])
|
198 |
-
if item.get('thumb') and os.path.exists(item['thumb']):
|
199 |
-
os.remove(item['thumb'])
|
200 |
|
201 |
del BATCH_JOBS[batch_id]
|
202 |
|
@@ -205,37 +211,17 @@ async def handle_batch_completion(batch_id: str):
|
|
205 |
async def start_handler(message: Message):
|
206 |
text = (
|
207 |
"π <b>Welcome to the Terabox Downloader Bot!</b>\n\n"
|
208 |
-
"π₯ Send me any valid Terabox link
|
209 |
-
f"π’
|
210 |
-
"π
|
211 |
-
"
|
212 |
-
"
|
213 |
-
"- Progress bar + ETA\n"
|
214 |
-
"- No ffmpeg re-encode\n"
|
215 |
-
"- Auto forward\n"
|
216 |
-
"\nβ
<i>Send links below:</i>"
|
217 |
)
|
218 |
-
await
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
if message.from_user.id != config.OWNER_ID:
|
223 |
-
return
|
224 |
-
|
225 |
-
users = await db_utils.get_all_active_user_ids_db()
|
226 |
-
uptime_sec = int(time.time() - BOT_START_TIME)
|
227 |
-
|
228 |
-
text = (
|
229 |
-
f"π
<b>Admin Panel</b>\n\n"
|
230 |
-
f"π° Uptime: {uptime_sec} sec\n"
|
231 |
-
f"π Total users: {len(users)}\n"
|
232 |
-
f"π’ Queue size: {TASK_QUEUE.qsize()}\n"
|
233 |
-
f"π§ Workers: {config.CONCURRENT_WORKERS}\n"
|
234 |
-
f"π Forward Channel: {config.FORWARD_CHANNEL_ID}\n"
|
235 |
-
f"π’ Force Sub Channel: @{config.FORCE_SUB_CHANNEL_USERNAME}\n"
|
236 |
-
f"π¨βπ‘ Worker URL: {config.TERABOX_WORKER_URL}"
|
237 |
)
|
238 |
-
await message.reply(text)
|
239 |
|
240 |
@dp.message(F.text | F.caption)
|
241 |
async def message_handler(message: Message):
|
@@ -248,7 +234,7 @@ async def message_handler(message: Message):
|
|
248 |
message.from_user.first_name
|
249 |
)
|
250 |
|
251 |
-
links = list(set(re.findall(r'https?://[^\s<>"]+', message.text or message.caption or "")))
|
252 |
terabox_links = [link for link in links if any(domain in link for domain in [
|
253 |
"terabox.com", "teraboxapp.com", "terasharelink.com", "1024tera.com",
|
254 |
"freeterabox.com", "4funbox.com", "box-links.com"
|
@@ -258,8 +244,13 @@ async def message_handler(message: Message):
|
|
258 |
return
|
259 |
|
260 |
batch_id = str(uuid.uuid4())
|
261 |
-
|
262 |
-
|
|
|
|
|
|
|
|
|
|
|
263 |
reply_to_message_id=message.message_id
|
264 |
)
|
265 |
|
@@ -271,7 +262,7 @@ async def message_handler(message: Message):
|
|
271 |
"source_chat_id": message.chat.id,
|
272 |
"source_user_id": message.from_user.id,
|
273 |
"source_message_id": message.message_id,
|
274 |
-
"
|
275 |
"lock": asyncio.Lock(),
|
276 |
"start_time": time.time()
|
277 |
}
|
@@ -290,5 +281,4 @@ def start_bot():
|
|
290 |
dp.startup.register(on_startup)
|
291 |
dp.message.register(message_handler)
|
292 |
dp.message.register(start_handler, CommandStart())
|
293 |
-
dp.message.register(admin_handler, Command("admin"))
|
294 |
return dp, bot
|
|
|
1 |
+
# bot.py
|
2 |
+
|
3 |
import asyncio
|
4 |
import logging
|
5 |
import os
|
|
|
9 |
from typing import Dict
|
10 |
|
11 |
from aiogram import Bot, Dispatcher, types, F
|
12 |
+
from aiogram.filters import CommandStart
|
13 |
from aiogram.types import Message, FSInputFile
|
14 |
from aiogram.enums import ParseMode
|
15 |
from aiogram.exceptions import TelegramBadRequest
|
|
|
44 |
dp = Dispatcher()
|
45 |
TASK_QUEUE = asyncio.Queue()
|
46 |
BATCH_JOBS: Dict[str, Dict] = {}
|
|
|
47 |
|
48 |
# --- Worker Logic ---
|
49 |
async def link_processor_worker(worker_id: int):
|
|
|
74 |
if error:
|
75 |
raise ValueError(error)
|
76 |
|
77 |
+
local_filepath, download_error = await terabox.download_terabox_file(download_url, raw_filename)
|
|
|
|
|
|
|
78 |
if download_error:
|
79 |
raise ValueError(download_error)
|
80 |
|
|
|
83 |
'path': local_filepath,
|
84 |
'name': raw_filename,
|
85 |
'size': os.path.getsize(local_filepath),
|
86 |
+
'short_id': short_id
|
|
|
87 |
}
|
88 |
|
89 |
except Exception as e:
|
|
|
97 |
batch_info["failed_links"].append({"link": original_link, "error": error_msg})
|
98 |
|
99 |
processed, total = batch_info['processed_links'], batch_info['total_links']
|
100 |
+
|
101 |
+
eta_sec = max(1, int((time.time() - batch_info["start_time"]) / processed * (total - processed)))
|
102 |
|
103 |
try:
|
104 |
+
await bot.edit_message_text(
|
105 |
+
chat_id=batch_info["source_chat_id"],
|
106 |
+
message_id=batch_info["status_message_id"],
|
107 |
+
text=(
|
108 |
+
f"βοΈ Batch <code>{batch_id[:6]}</code> in progress... "
|
109 |
+
f"{processed}/{total} links processed.\n"
|
110 |
+
f"β³ ETA: ~{eta_sec}s"
|
111 |
+
)
|
112 |
)
|
113 |
except TelegramBadRequest:
|
114 |
pass
|
|
|
119 |
|
120 |
TASK_QUEUE.task_done()
|
121 |
|
122 |
+
async def send_and_cache_file(chat_id: int, file_info: dict, caption: str) -> Message:
|
123 |
file_path_or_id = file_info.get('file_id') or FSInputFile(file_info['path'], filename=file_info['name'])
|
124 |
media_type = file_info.get('type')
|
125 |
filename = file_info.get('name') or file_info.get('filename')
|
126 |
|
127 |
+
sent_message = await bot.send_document(chat_id, file_path_or_id, caption=caption)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
|
129 |
if not file_info.get('cached') and sent_message:
|
130 |
+
file_id_to_cache = sent_message.document.file_id
|
131 |
await db_utils.add_to_cache(
|
132 |
+
file_info['short_id'], file_id_to_cache, filename, 'document', file_info['size']
|
133 |
)
|
134 |
|
135 |
return sent_message
|
|
|
139 |
if not batch:
|
140 |
return
|
141 |
|
|
|
142 |
successful_downloads = batch["successful_downloads"]
|
143 |
|
144 |
try:
|
145 |
if not successful_downloads:
|
146 |
failed_links_text = "\n".join(
|
147 |
+
[f"- {x['link']} β {x['error']}" for x in batch['failed_links']]
|
148 |
)
|
149 |
+
await bot.edit_message_text(
|
150 |
+
chat_id=batch["source_chat_id"],
|
151 |
+
message_id=batch["status_message_id"],
|
152 |
+
text=(
|
153 |
+
f"β Batch <code>{batch_id[:6]}</code> failed. No files could be processed.\n"
|
154 |
+
f"<b>Details:</b>\n{failed_links_text}"
|
155 |
+
)
|
156 |
)
|
157 |
return
|
158 |
|
159 |
+
await bot.edit_message_text(
|
160 |
+
chat_id=batch["source_chat_id"],
|
161 |
+
message_id=batch["status_message_id"],
|
162 |
+
text=(
|
163 |
+
f"β
Batch <code>{batch_id[:6]}</code> downloaded. Sending {len(successful_downloads)} files..."
|
164 |
+
)
|
165 |
)
|
166 |
|
167 |
+
# Forward original message if enabled
|
168 |
if config.FORWARD_CHANNEL_ID:
|
169 |
await bot.forward_message(
|
170 |
config.FORWARD_CHANNEL_ID,
|
|
|
173 |
)
|
174 |
for item in successful_downloads:
|
175 |
caption = f"`{item.get('name') or item.get('filename')}`"
|
176 |
+
await send_and_cache_file(config.FORWARD_CHANNEL_ID, item, caption)
|
177 |
await asyncio.sleep(1)
|
178 |
|
179 |
for item in successful_downloads:
|
180 |
caption = f"`{item.get('name') or item.get('filename')}`"
|
181 |
+
await send_and_cache_file(batch["source_chat_id"], item, caption)
|
182 |
|
183 |
+
summary = f"β
Batch <code>{batch_id[:6]}</code> complete: {len(successful_downloads)} files sent."
|
184 |
if batch["failed_links"]:
|
185 |
summary += f"\nβ {len(batch['failed_links'])} links failed."
|
186 |
|
187 |
+
await bot.edit_message_text(
|
188 |
+
chat_id=batch["source_chat_id"],
|
189 |
+
message_id=batch["status_message_id"],
|
190 |
+
text=summary
|
191 |
+
)
|
192 |
|
193 |
except Exception as e:
|
194 |
logger.error(f"Error during batch completion for {batch_id}: {e}", exc_info=True)
|
195 |
+
await bot.edit_message_text(
|
196 |
+
chat_id=batch["source_chat_id"],
|
197 |
+
message_id=batch["status_message_id"],
|
198 |
+
text=(
|
199 |
+
f"β οΈ A critical error occurred while sending files for batch <code>{batch_id[:6]}</code>."
|
200 |
+
)
|
201 |
)
|
202 |
finally:
|
203 |
for item in successful_downloads:
|
204 |
if not item.get('cached') and os.path.exists(item['path']):
|
205 |
os.remove(item['path'])
|
|
|
|
|
206 |
|
207 |
del BATCH_JOBS[batch_id]
|
208 |
|
|
|
211 |
async def start_handler(message: Message):
|
212 |
text = (
|
213 |
"π <b>Welcome to the Terabox Downloader Bot!</b>\n\n"
|
214 |
+
"π₯ Send me any valid Terabox link and I will fetch the file and send it to you.\n\n"
|
215 |
+
f"π’ Please make sure you are a member of: @{config.FORCE_SUB_CHANNEL_USERNAME}\n\n"
|
216 |
+
"π Supports batch links & auto-caching.\n"
|
217 |
+
"πΎ Fast & lightweight.\n\n"
|
218 |
+
"β
<i>Just send your link below β¬οΈ</i>"
|
|
|
|
|
|
|
|
|
219 |
)
|
220 |
+
await bot.send_message(
|
221 |
+
chat_id=message.chat.id,
|
222 |
+
text=text,
|
223 |
+
reply_to_message_id=message.message_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
)
|
|
|
225 |
|
226 |
@dp.message(F.text | F.caption)
|
227 |
async def message_handler(message: Message):
|
|
|
234 |
message.from_user.first_name
|
235 |
)
|
236 |
|
237 |
+
links = list(set(re.findall(r'https?://[^\s<>"\']+', message.text or message.caption or "")))
|
238 |
terabox_links = [link for link in links if any(domain in link for domain in [
|
239 |
"terabox.com", "teraboxapp.com", "terasharelink.com", "1024tera.com",
|
240 |
"freeterabox.com", "4funbox.com", "box-links.com"
|
|
|
244 |
return
|
245 |
|
246 |
batch_id = str(uuid.uuid4())
|
247 |
+
|
248 |
+
status_msg = await bot.send_message(
|
249 |
+
chat_id=message.chat.id,
|
250 |
+
text=(
|
251 |
+
f"β
Found {len(terabox_links)} links. Queued as batch <code>{batch_id[:6]}</code>.\n"
|
252 |
+
f"β³ Please wait..."
|
253 |
+
),
|
254 |
reply_to_message_id=message.message_id
|
255 |
)
|
256 |
|
|
|
262 |
"source_chat_id": message.chat.id,
|
263 |
"source_user_id": message.from_user.id,
|
264 |
"source_message_id": message.message_id,
|
265 |
+
"status_message_id": status_msg.message_id,
|
266 |
"lock": asyncio.Lock(),
|
267 |
"start_time": time.time()
|
268 |
}
|
|
|
281 |
dp.startup.register(on_startup)
|
282 |
dp.message.register(message_handler)
|
283 |
dp.message.register(start_handler, CommandStart())
|
|
|
284 |
return dp, bot
|