Reooiq commited on
Commit
5db845f
·
verified ·
1 Parent(s): 16b62e0

Create app py

Browse files
Files changed (1) hide show
  1. app py +216 -0
app py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import json
4
+ import logging
5
+ import threading
6
+ import requests
7
+ from bs4 import BeautifulSoup
8
+ from flask import Flask
9
+
10
+ # ==========================================
11
+ # ⚙️ الإعدادات والمتغيرات (تم توليدها تلقائياً)
12
+ # ==========================================
13
+ SNAPCHAT_USER = "Ah_md107"
14
+ TELEGRAM_BOT_TOKEN = "8390641764:AAES8DscPm5HvSWUBqVyK-Ss9saw-Ppgzc4"
15
+ TELEGRAM_CHAT_ID = "-5264228773"
16
+ CHECK_INTERVAL = 15 * 60 # بالثواني
17
+
18
+ # إعداد ملف تسجيل البيانات المنشورة لمنع التكرار
19
+ POSTED_STORIES_FILE = "posted_stories.json"
20
+
21
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
+
23
+ app = Flask(__name__)
24
+
25
+ def load_posted_stories():
26
+ if os.path.exists(POSTED_STORIES_FILE):
27
+ try:
28
+ with open(POSTED_STORIES_FILE, "r", encoding="utf-8") as f:
29
+ return json.load(f)
30
+ except Exception as e:
31
+ logging.error(f"خطأ في قراءة ملف السجل: {e}")
32
+ return []
33
+
34
+ def save_posted_stories(posted_list):
35
+ try:
36
+ # نحتفظ بآخر 200 قصة فقط لعدم استهلاك المساحة
37
+ with open(POSTED_STORIES_FILE, "w", encoding="utf-8") as f:
38
+ json.dump(posted_list[-200:], f, ensure_ascii=False, indent=2)
39
+ except Exception as e:
40
+ logging.error(f"خطأ في حفظ ملف السجل: {e}")
41
+
42
+ def send_to_telegram(media_url, is_video, caption):
43
+ """إرسال الوسائط إلى جروب/قناة التليجرام باستخدام Telegram Bot API المباشر"""
44
+ base_url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/"
45
+
46
+ try:
47
+ # تحميل الوسيطة في الذاكرة لتجاوز حماية الروابط المباشرة لسناب شات
48
+ logging.info(f"جاري تحميل الوسائط من سناب شات لإرسالها للتليجرام...")
49
+ headers = {
50
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
51
+ }
52
+ media_response = requests.get(media_url, headers=headers, timeout=30)
53
+
54
+ if media_response.status_code != 200:
55
+ logging.error("فشل في تحميل محتوى القصة من الرابط.")
56
+ return False
57
+
58
+ files = {}
59
+ data = {'chat_id': TELEGRAM_CHAT_ID, 'caption': caption}
60
+
61
+ if is_video:
62
+ endpoint = base_url + "sendVideo"
63
+ files['video'] = ('story.mp4', media_response.content, 'video/mp4')
64
+ else:
65
+ endpoint = base_url + "sendPhoto"
66
+ files['photo'] = ('story.jpg', media_response.content, 'image/jpeg')
67
+
68
+ logging.info("جاري الإرسال إلى تليجرام...")
69
+ resp = requests.post(endpoint, data=data, files=files, timeout=40)
70
+
71
+ if resp.status_code == 200:
72
+ logging.info("✅ تم إرسال القصة إلى تليجرام بنجاح!")
73
+ return True
74
+ else:
75
+ logging.error(f"⚠️ خطأ من تليجرام: {resp.text}")
76
+ return False
77
+
78
+ except Exception as e:
79
+ logging.error(f"حدث خطأ أثناء الإرسال للتليجرام: {e}")
80
+ return False
81
+
82
+ def scrape_snapchat_stories():
83
+ """استخراج قصص سناب شات العامة من واجهة الويب (تعمل لعام 2026)"""
84
+ url = f"https://www.snapchat.com/add/{SNAPCHAT_USER}"
85
+ headers = {
86
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
87
+ "Accept-Language": "en-US,en;q=0.9,ar;q=0.8"
88
+ }
89
+
90
+ try:
91
+ logging.info(f"فحص الحساب: {SNAPCHAT_USER} ...")
92
+ res = requests.get(url, headers=headers, timeout=20)
93
+ if res.status_code == 404:
94
+ logging.warning("الحساب غير موجود أو غير متاح للعامة.")
95
+ return []
96
+
97
+ soup = BeautifulSoup(res.text, 'html.parser')
98
+
99
+ # سناب شات يضع بيانات القصص في كود JSON مدمج في الصفحة
100
+ script_data = soup.find('script', id='__NEXT_DATA__')
101
+ if not script_data:
102
+ # محاولة البحث عن عنصر JSON البديل لعام 2026
103
+ script_data = soup.find('script', {'type': 'application/json'})
104
+
105
+ if not script_data:
106
+ logging.warning("لم يتم العثور على بيانات القصص في الصفحة.")
107
+ return []
108
+
109
+ json_content = json.loads(script_data.string)
110
+
111
+ # استخراج مصفوفة القصص (تختلف الهيكلية قليلاً حسب التحديثات، نبحث في الـ props)
112
+ stories = []
113
+ try:
114
+ # تتبع المسار المحتمل لقصص المستخدم
115
+ user_profile = json_content.get('props', {}).get('pageProps', {}).get('userProfile', {})
116
+ public_stories = user_profile.get('publicStories', [])
117
+
118
+ # إذا لم تكن موجودة، قد تكون في state
119
+ if not public_stories:
120
+ snap_list = json_content.get('props', {}).get('pageProps', {}).get('story', {}).get('snaps', [])
121
+ public_stories = snap_list
122
+
123
+ for snap in public_stories:
124
+ snap_id = snap.get('id') or snap.get('snapId')
125
+ media_url = snap.get('mediaUrl') or snap.get('url')
126
+ media_type = snap.get('mediaType', '') # VIDEO or IMAGE
127
+ timestamp = snap.get('timestampInSec') or snap.get('creationTimestamp')
128
+
129
+ if snap_id and media_url:
130
+ is_vid = "VIDEO" in str(media_type).upper() or ".mp4" in media_url
131
+ stories.append({
132
+ "id": str(snap_id),
133
+ "url": media_url,
134
+ "is_video": is_vid,
135
+ "time": timestamp
136
+ })
137
+ except Exception as ex:
138
+ logging.error(f"فشل في تحليل مسار الـ JSON: {ex}")
139
+
140
+ return stories
141
+
142
+ except Exception as e:
143
+ logging.error(f"خطأ أثناء الاتصال بسناب شات: {e}")
144
+ return []
145
+
146
+ def bot_worker():
147
+ """الحلقة التكرارية لعمل البوت في الخلفية"""
148
+ logging.info("🚀 بدء تشغيل عامل البوت في الخلفية...")
149
+
150
+ # الانتظار قليلاً حتى يعمل الخادم
151
+ time.sleep(5)
152
+
153
+ while True:
154
+ posted_ids = load_posted_stories()
155
+ current_stories = scrape_snapchat_stories()
156
+
157
+ logging.info(f"تم العثور على {len(current_stories)} قصة عامة متاحة في صفحة الويب.")
158
+
159
+ new_posted = False
160
+ for story in current_stories:
161
+ s_id = story["id"]
162
+ if s_id not in posted_ids:
163
+ logging.info(f"🌟 قصة جديدة تم رصدها! المعرف: {s_id[:10]}...")
164
+
165
+ # تجهيز النص المرفق
166
+ caption = f"👻 منشور جديد من <b>{SNAPCHAT_USER}</b>\n\n🔗 <a href='https://www.snapchat.com/add/{SNAPCHAT_USER}'>مشاهدة الحساب على سناب شات</a>"
167
+
168
+ success = send_to_telegram(story["url"], story["is_video"], caption)
169
+ if success:
170
+ posted_ids.append(s_id)
171
+ new_posted = True
172
+ # الانتظار بين إرسال كل قصة لتجنب ضغط التليجرام
173
+ time.sleep(3)
174
+
175
+ if new_posted:
176
+ save_posted_stories(posted_ids)
177
+
178
+ logging.info(f"💤 الدخول في وضع الانتظار لمدة {CHECK_INTERVAL // 60} دقيقة...")
179
+ time.sleep(CHECK_INTERVAL)
180
+
181
+ # ==========================================
182
+ # 🌐 خادم الويب للحفاظ على تشغيل Hugging Face
183
+ # ==========================================
184
+ @app.route('/')
185
+ def home():
186
+ return f"""
187
+ <html>
188
+ <head>
189
+ <title>Snap2Tele Bot Status</title>
190
+ <meta charset="utf-8">
191
+ <style>
192
+ body {{ font-family: system-ui; text-align: center; padding: 50px; background: #f8fafc; color: #0f172a; }}
193
+ .status {{ display: inline-block; padding: 10px 20px; background: #22c55e; color: white; border-radius: 50px; font-weight: bold; }}
194
+ </style>
195
+ </head>
196
+ <body>
197
+ <h1>🚀 البوت يعمل بنجاح</h1>
198
+ <p class="status">● ONLINE</p>
199
+ <p>يتم مراقبة حساب سناب شات: <b>{SNAPCHAT_USER}</b></p>
200
+ <p>لإبقاء البوت يعمل 24/7 مجاناً، أضف هذا الرابط إلى موقع <b>cron-job.org</b></p>
201
+ </body>
202
+ </html>
203
+ """
204
+
205
+ @app.route('/health')
206
+ def health():
207
+ return {"status": "ok", "user": SNAPCHAT_USER, "timestamp": time.time()}
208
+
209
+ if __name__ == '__main__':
210
+ # تشغيل مراقب السناب شات في Thread منفصل حتى لا يتعارض مع خادم الويب
211
+ worker_thread = threading.Thread(target=bot_worker, daemon=True)
212
+ worker_thread.start()
213
+
214
+ # تشغيل خادم Flask على المنفذ 7860 المتوافق مع Hugging Face Spaces
215
+ port = int(os.environ.get('PORT', 7860))
216
+ app.run(host='0.0.0.0', port=port)