Asrasahar commited on
Commit
0824edc
·
verified ·
1 Parent(s): 35a83f3

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -0
app.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import requests
4
+ import io
5
+ import logging
6
+ import threading # برای اجرای همزمان ربات و وب اپلیکیشن
7
+ from flask import Flask, request, jsonify, send_from_directory
8
+ from huggingface_hub import HfApi
9
+ import mimetypes
10
+
11
+ # کتابخانه های جدید برای ربات تلگرام
12
+ from telegram import Update, File as TelegramFile
13
+ from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext
14
+
15
+ # 1. تنظیمات لاگینگ برای نمایش پیام ها در کنسول اسپیس
16
+ logging.basicConfig(
17
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
18
+ level=logging.INFO
19
+ )
20
+ logger = logging.getLogger(__name__)
21
+
22
+ app = Flask(__name__, static_folder='static', static_url_path='/static')
23
+
24
+ # 2. خواندن متغیرهای محیطی
25
+ HF_TOKEN = os.getenv("HF_TOKEN")
26
+ TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN") # <-- توکن ربات تلگرام
27
+ HF_REPO_ID = "Asrasahar/Ezmary-uploads" # <-- آدرس دیتاسنت شما
28
+
29
+ # 3. بررسی وجود توکن ها
30
+ if not HF_TOKEN:
31
+ logger.error("FATAL ERROR: HF_TOKEN environment variable not set!")
32
+ exit()
33
+ if not TELEGRAM_TOKEN:
34
+ logger.error("FATAL ERROR: TELEGRAM_TOKEN environment variable not set! Please set it in your Space's 'Repository secrets'.")
35
+ exit()
36
+
37
+ # 4. ایجاد یک نمونه از HfApi
38
+ hf_api = HfApi(token=HF_TOKEN)
39
+ logger.info(f"Flask app initialized. Target dataset repository: {HF_REPO_ID}")
40
+
41
+ # --- توابع کمکی (بدون تغییر) ---
42
+ def get_folder_by_mimetype(mimetype):
43
+ """بر اساس نوع MIME، نام پوشه مقصد را برمی‌گرداند."""
44
+ if not mimetype:
45
+ return "others"
46
+ main_type = mimetype.split('/')[0]
47
+ if main_type == "image": return "images"
48
+ elif main_type == "video": return "videos"
49
+ elif main_type == "audio": return "audios"
50
+ elif main_type == "application":
51
+ if 'pdf' in mimetype: return "documents/pdfs"
52
+ elif 'zip' in mimetype or 'rar' in mimetype or '7z' in mimetype: return "archives"
53
+ else: return "documents/applications"
54
+ elif main_type == "text": return "documents/texts"
55
+ else: return "others"
56
+
57
+ # --- تابع اصلی آپلود (بازنویسی شده برای استفاده مشترک) ---
58
+ def process_and_upload(file_content: bytes, original_filename: str, mimetype: str) -> str:
59
+ """
60
+ محتوای فایل را گرفته، پردازش کرده و به Hugging Face آپلود می‌کند.
61
+ لینک مستقیم فایل آپلود شده را برمی‌گرداند.
62
+ """
63
+ # ایجاد نام فایل منحصر به فرد
64
+ file_extension = os.path.splitext(original_filename)[1] or mimetypes.guess_extension(mimetype) or ""
65
+ filename = f"{uuid.uuid4().hex}{file_extension}"
66
+
67
+ # تعیین پوشه مقصد
68
+ target_folder = get_folder_by_mimetype(mimetype)
69
+ path_in_repo = f"{target_folder}/{filename}"
70
+
71
+ # آپلود به Hugging Face Hub
72
+ file_stream = io.BytesIO(file_content)
73
+ logger.info(f"Uploading to Hugging Face: {path_in_repo}")
74
+ hf_api.upload_file(
75
+ path_or_fileobj=file_stream,
76
+ path_in_repo=path_in_repo,
77
+ repo_id=HF_REPO_ID,
78
+ repo_type="dataset",
79
+ )
80
+
81
+ direct_url = f"https://huggingface.co/datasets/{HF_REPO_ID}/resolve/main/{path_in_repo}"
82
+ logger.info(f"File uploaded successfully! URL: {direct_url}")
83
+
84
+ return direct_url
85
+
86
+ # --- بخش Flask (برای وب سایت) ---
87
+
88
+ @app.route('/')
89
+ def serve_html():
90
+ return send_from_directory(app.static_folder, 'index.html')
91
+
92
+ @app.route('/upload', methods=['POST'])
93
+ def upload_file_endpoint():
94
+ try:
95
+ # حالت اول: آپلود از کامپیوتر
96
+ if 'file' in request.files:
97
+ uploaded_file = request.files['file']
98
+ if uploaded_file.filename == '':
99
+ return jsonify({"error": "No selected file"}), 400
100
+
101
+ file_content = uploaded_file.read()
102
+ direct_url = process_and_upload(file_content, uploaded_file.filename, uploaded_file.mimetype)
103
+ return jsonify({"message": "File uploaded successfully!", "hf_url": direct_url}), 200
104
+
105
+ # حالت دوم: آپلود از طریق لینک
106
+ elif request.is_json and 'url' in request.json:
107
+ file_url = request.json['url']
108
+ if not file_url:
109
+ return jsonify({"error": "No URL provided"}), 400
110
+
111
+ logger.info(f"Downloading file from URL: {file_url}")
112
+ response = requests.get(file_url, stream=True, timeout=30)
113
+ response.raise_for_status()
114
+
115
+ file_content = response.content
116
+ mimetype = response.headers.get('Content-Type')
117
+ original_filename = file_url.split('/')[-1].split('?')[0]
118
+
119
+ direct_url = process_and_upload(file_content, original_filename, mimetype)
120
+ return jsonify({"message": "File uploaded successfully!", "hf_url": direct_url}), 200
121
+
122
+ else:
123
+ return jsonify({"error": "Invalid request"}), 400
124
+
125
+ except requests.exceptions.RequestException as e:
126
+ logger.error(f"Failed to download from URL: {e}", exc_info=True)
127
+ return jsonify({"error": f"Failed to download from URL: {str(e)}"}), 400
128
+ except Exception as e:
129
+ logger.error(f"An internal server error occurred: {e}", exc_info=True)
130
+ return jsonify({"error": f"An internal server error occurred: {str(e)}"}), 500
131
+
132
+ # --- بخش ربات تلگرام ---
133
+
134
+ def start(update: Update, context: CallbackContext) -> None:
135
+ """پاسخ به دستور /start"""
136
+ welcome_message = "سلام! من ربات آپلودر فایل هستم.\n\n" \
137
+ "فایل (عکس، ویدیو، داکیومنت و...) یا لینک مستقیم آن را برای من بفرستید تا آن را برای شما به یک لینک مستقیم دائمی تبدیل کنم."
138
+ update.message.reply_text(welcome_message)
139
+
140
+ def handle_file(update: Update, context: CallbackContext) -> None:
141
+ """پردازش فایل های ارسالی (داکیومنت، ویدیو، عکس و...)"""
142
+ message = update.message
143
+ file_to_process = message.document or message.video or message.audio or (message.photo[-1] if message.photo else None)
144
+
145
+ if not file_to_process:
146
+ message.reply_text("نوع فایل ارسالی پشتیبانی نمی‌شود.")
147
+ return
148
+
149
+ # ارسال پیام "در حال پردازش" به کاربر
150
+ status_message = message.reply_text("فایل دریافت شد، در حال پردازش و آپلود...")
151
+
152
+ try:
153
+ tg_file: TelegramFile = file_to_process.get_file()
154
+ file_content = tg_file.download_as_bytearray()
155
+
156
+ # نام فایل و نوع آن را از تلگرام می گیریم
157
+ original_filename = file_to_process.file_name if hasattr(file_to_process, 'file_name') else tg_file.file_path.split('/')[-1]
158
+ mimetype = file_to_process.mime_type or mimetypes.guess_type(original_filename)[0]
159
+
160
+ logger.info(f"Received file from Telegram: {original_filename} (MIME: {mimetype})")
161
+
162
+ # آپلود فایل با استفاده از تابع مشترک
163
+ direct_url = process_and_upload(bytes(file_content), original_filename, mimetype)
164
+
165
+ # ویرایش پیام "در حال پردازش" و ارسال لینک نهایی
166
+ status_message.edit_text(f"✅ آپلود با موفقیت انجام شد!\n\nلینک مستقیم شما:\n{direct_url}")
167
+
168
+ except Exception as e:
169
+ logger.error(f"Error processing Telegram file: {e}", exc_info=True)
170
+ status_message.edit_text(f"❌ خطا در پردازش فایل: {e}")
171
+
172
+ def handle_text(update: Update, context: CallbackContext) -> None:
173
+ """پردازش پیام های متنی (برای لینک ها)"""
174
+ message = update.message
175
+ url = message.text.strip()
176
+
177
+ # بررسی اینکه آیا متن یک لینک معتبر است یا نه
178
+ if url.startswith(('http://', 'https://')):
179
+ status_message = message.reply_text("لینک دریافت شد، در حال دانلود و آپلود...")
180
+ try:
181
+ logger.info(f"Downloading file from URL via Telegram: {url}")
182
+ response = requests.get(url, stream=True, timeout=30)
183
+ response.raise_for_status()
184
+
185
+ file_content = response.content
186
+ mimetype = response.headers.get('Content-Type')
187
+ original_filename = url.split('/')[-1].split('?')[0]
188
+
189
+ # آپلود فایل با استفاده از تابع مشترک
190
+ direct_url = process_and_upload(file_content, original_filename, mimetype)
191
+ status_message.edit_text(f"✅ آپلود از لینک با موفقیت انجام شد!\n\nلینک مستقیم شما:\n{direct_url}")
192
+
193
+ except requests.exceptions.RequestException as e:
194
+ logger.error(f"Failed to download from URL (Telegram): {e}", exc_info=True)
195
+ status_message.edit_text(f"❌ خطا در دانلود از لینک: {e}")
196
+ except Exception as e:
197
+ logger.error(f"An internal error occurred (Telegram URL): {e}", exc_info=True)
198
+ status_message.edit_text(f"❌ خطا در پردازش لینک: {e}")
199
+ else:
200
+ message.reply_text("لطفا یک فایل یا یک لینک معتبر ارسال کنید.")
201
+
202
+
203
+ def run_bot():
204
+ """تابع اصلی برای راه اندازی و اجرای ربات تلگرام"""
205
+ updater = Updater(TELEGRAM_TOKEN)
206
+ dispatcher = updater.dispatcher
207
+
208
+ # تعریف دستورات و پاسخ ها
209
+ dispatcher.add_handler(CommandHandler("start", start))
210
+ dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, handle_text))
211
+ dispatcher.add_handler(MessageHandler(Filters.document | Filters.video | Filters.photo | Filters.audio, handle_file))
212
+
213
+ # راه اندازی ربات
214
+ updater.start_polling()
215
+ logger.info("Telegram bot started polling...")
216
+ # updater.idle() # این خط لازم نیست چون وب سرور برنامه را فعال نگه می دارد
217
+
218
+ # --- راه اندازی برنامه ---
219
+
220
+ # اجرای ربات در یک ترد (نخ) جداگانه تا با وب سرور تداخل نداشته باشد
221
+ bot_thread = threading.Thread(target=run_bot)
222
+ bot_thread.daemon = True # با بستن برنامه اصلی، این ترد هم بسته می شود
223
+ bot_thread.start()
224
+
225
+ # این بخش فقط برای اجرای محلی (local) است
226
+ # در Hugging Face Spaces، از gunicorn برای اجرای برنامه استفاده می شود
227
+ if __name__ == '__main__':
228
+ port = int(os.environ.get('PORT', 7860))
229
+ app.run(host='0.0.0.0', port=port, debug=False)