VLADIMIROFF777 commited on
Commit
adae9d7
·
verified ·
1 Parent(s): 3286340

Create funpay_loop.py

Browse files
Files changed (1) hide show
  1. funpay_loop.py +355 -0
funpay_loop.py ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+ import time
3
+ import requests
4
+ import json
5
+ import random
6
+ import logging
7
+ import sys
8
+ import traceback
9
+ from flask import Flask, jsonify, make_response
10
+ from bs4 import BeautifulSoup
11
+ from FunPayAPI.account import Account
12
+ from FunPayAPI.common import enums
13
+ try:
14
+ from FunPayAPI.updater.runner import Runner
15
+ except ImportError:
16
+ from FunPayAPI.account import Runner
17
+
18
+ # ==========================================
19
+ # 0. DEBUGGING INFRASTRUCTURE
20
+ # ==========================================
21
+ LOG_BUFFER = []
22
+
23
+ class BufferHandler(logging.Handler):
24
+ def emit(self, record):
25
+ try:
26
+ msg = self.format(record)
27
+ LOG_BUFFER.append(f"{time.strftime('%H:%M:%S', time.localtime())} - {msg}")
28
+ if len(LOG_BUFFER) > 200:
29
+ LOG_BUFFER.pop(0)
30
+ except Exception:
31
+ pass
32
+
33
+ logging.basicConfig(level=logging.INFO)
34
+ logger = logging.getLogger("FunPayBot")
35
+ logger.addHandler(BufferHandler())
36
+
37
+ # ==========================================
38
+ # 1. KEEP-ALIVE SERVER (Hugging Face)
39
+ # ==========================================
40
+ app = Flask(__name__)
41
+
42
+ @app.route('/')
43
+ def home():
44
+ lines = "\n".join(LOG_BUFFER)
45
+ # Return HTML for better display
46
+ return f"""
47
+ <h1>🤖 Bot Status: Running</h1>
48
+ <p>Go to <a href="/logs">/logs</a> to see full logs.</p>
49
+ <p>Go to <a href="/test_tg">/test_tg</a> to force test Telegram.</p>
50
+ <h3>Last 10 Logs:</h3>
51
+ <pre>{lines[-1000:]}</pre>
52
+ """
53
+
54
+ @app.route('/logs')
55
+ def get_logs():
56
+ return "<pre>" + "\n".join(LOG_BUFFER) + "</pre>"
57
+
58
+ @app.route('/test_tg')
59
+ def test_tg():
60
+ try:
61
+ url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
62
+ payload = {"chat_id": TELEGRAM_CHAT_ID, "text": "TEST MESSAGE FROM WEB. If you see this, connectivity works."}
63
+ r = requests.post(url, json=payload, timeout=5)
64
+ return f"OK: {r.status_code} {r.text}"
65
+ except Exception as e:
66
+ return f"ERROR: {e}"
67
+
68
+ def run_web():
69
+ logger.info("Keep-Alive Web Server started on port 7860")
70
+ app.run(host='0.0.0.0', port=7860)
71
+
72
+ t = threading.Thread(target=run_web)
73
+ t.start()
74
+
75
+ # ==========================================
76
+ # 2. PROXY MONKEY PATCH (Cloudflare)
77
+ # ==========================================
78
+ PROXY_URL = "https://damp-cell-4702.newnout6.workers.dev/"
79
+ original_request = requests.Session.request
80
+
81
+ def proxy_request(self, method, url, *args, **kwargs):
82
+ if "funpay.com" in url and PROXY_URL not in url:
83
+ target = url
84
+ valid_cookies = self.cookies.get_dict(domain=".funpay.com")
85
+ valid_cookies.update(self.cookies.get_dict(domain="funpay.com"))
86
+
87
+ kw_cookies = kwargs.get('cookies')
88
+ if kw_cookies: valid_cookies.update(kw_cookies)
89
+
90
+ headers = kwargs.get('headers', {}) or {}
91
+ headers = headers.copy()
92
+
93
+ found_cookie_key = None
94
+ for k in list(headers.keys()):
95
+ if k.lower() == 'cookie':
96
+ found_cookie_key = k
97
+ raw_c = headers[k]
98
+ for part in raw_c.split(';'):
99
+ if '=' in part:
100
+ kv = part.strip().split('=', 1)
101
+ if len(kv) == 2:
102
+ valid_cookies[kv[0]] = kv[1]
103
+ del headers[k]
104
+
105
+ cookie_header = "; ".join([f"{k}={v}" for k, v in valid_cookies.items()])
106
+
107
+ if cookie_header: headers['Cookie'] = cookie_header
108
+ if 'User-Agent' not in headers:
109
+ headers['User-Agent'] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
110
+
111
+ # Ensure RUB/RU locale
112
+ if 'cy' not in valid_cookies: valid_cookies['cy'] = 'rub'
113
+ if 'locale' not in valid_cookies: valid_cookies['locale'] = 'ru'
114
+
115
+ params = kwargs.get('params', {}) or {}
116
+ params = params.copy()
117
+ params['url'] = target
118
+ kwargs['headers'] = headers
119
+ kwargs['params'] = params
120
+ kwargs['cookies'] = {}
121
+
122
+ return original_request(self, method, PROXY_URL, *args, **kwargs)
123
+ return original_request(self, method, url, *args, **kwargs)
124
+
125
+ requests.Session.request = proxy_request
126
+ requests.request = proxy_request
127
+
128
+ # ==========================================
129
+ # 3. BOT LOGIC
130
+ # ==========================================
131
+ TELEGRAM_BOT_TOKEN = "8203118488:AAGC1EQavBt0u9suYQ32qt4owkU_VuubWG0"
132
+ TELEGRAM_CHAT_ID = "1517760699"
133
+ API_URL = "http://shrvld789.pythonanywhere.com"
134
+
135
+ # Default Key
136
+ GOLDEN_KEY = "dummy_key_waiting_for_update"
137
+ DUMP_CATEGORY_URL = "https://funpay.com/lots/1355/"
138
+ MY_USERNAME = "" # Will be filled after login
139
+
140
+ def send_telegram_notification(message):
141
+ try:
142
+ url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
143
+ payload = {"chat_id": TELEGRAM_CHAT_ID, "text": message, "parse_mode": "HTML"}
144
+ logger.info(f"Adding to TG queue: {message[:20]}...")
145
+ requests.post(url, json=payload, timeout=10)
146
+ except Exception as e:
147
+ logger.error(f"Failed to send Telegram notification: {e}")
148
+
149
+ def get_golden_key_from_api():
150
+ try:
151
+ url = f"{API_URL}/api/config"
152
+ resp = requests.get(url, timeout=10)
153
+ if resp.status_code == 200:
154
+ config = resp.json()
155
+ key = config.get('golden_key')
156
+ if key and len(key) > 10:
157
+ return key
158
+ except Exception as e:
159
+ logger.error(f"Failed to fetch key from API: {e}")
160
+ return None
161
+
162
+ def get_product_from_api(quantity, buyer_name, price):
163
+ try:
164
+ url = f"{API_URL}/api/sell"
165
+ payload = {"quantity": quantity, "customer_name": buyer_name, "price": price}
166
+ response = requests.post(url, json=payload, timeout=20)
167
+
168
+ if response.status_code == 200:
169
+ data = response.json()
170
+ if data.get('success'):
171
+ return "\n".join(data.get('accounts', []))
172
+ return None
173
+ except Exception as e:
174
+ logger.error(f"Error requesting product from API: {e}")
175
+ return None
176
+
177
+ def send_heartbeat():
178
+ """Periodically sends a heartbeat to the Admin Bot"""
179
+ while True:
180
+ try:
181
+ requests.post(f"{API_URL}/api/heartbeat", timeout=5)
182
+ except Exception as e:
183
+ logger.error(f"Heartbeat failed: {e}")
184
+ time.sleep(60)
185
+
186
+ def dump_price_check():
187
+ """Monitors competitor prices and suggests updates"""
188
+ global MY_USERNAME
189
+ logger.info("Initializing Price Monitor...")
190
+
191
+ # Wait for username with attempts
192
+ attempts = 0
193
+ while not MY_USERNAME:
194
+ attempts += 1
195
+ if attempts % 10 == 0:
196
+ logger.warning("Still waiting for MY_USERNAME...")
197
+ time.sleep(5)
198
+
199
+ logger.info(f"Starting Price Monitor for {MY_USERNAME}...")
200
+
201
+ while True:
202
+ try:
203
+ wait_time = random.randint(300, 420)
204
+ logger.info(f"Checking prices... Next check in {wait_time}s")
205
+
206
+ # Manually fetch page
207
+ session = requests.Session()
208
+ headers = {
209
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
210
+ "Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"
211
+ }
212
+ resp = session.get(DUMP_CATEGORY_URL, headers=headers, cookies={'cy': 'rub', 'locale': 'ru'}, timeout=20)
213
+
214
+ if resp.status_code == 200:
215
+ soup = BeautifulSoup(resp.text, "html.parser")
216
+ offers = soup.find_all("a", class_="tc-item")
217
+ online_competitor_prices = []
218
+ my_current_price = None
219
+
220
+ for offer in offers:
221
+ # Filter 1: Check Online Status
222
+ if offer.get("data-online") != "1":
223
+ continue
224
+
225
+ # Filter 2: Check Subscription (Must include "с подпиской")
226
+ subscription = offer.get("data-f-subscription", "").lower()
227
+ if "подписк" not in subscription or "без" in subscription:
228
+ continue
229
+
230
+ # Filter 3: Check Keywords in Description
231
+ desc_div = offer.find("div", class_="tc-desc-text")
232
+ desc = desc_div.get_text(strip=True).lower() if desc_div else ""
233
+
234
+ # Filter: Reviews
235
+ rating_div = offer.find("span", class_="rating-mini-count")
236
+ if rating_div:
237
+ try:
238
+ reviews = int(rating_div.get_text(strip=True).replace(' ', ''))
239
+ if reviews > 100: continue
240
+ except: pass
241
+
242
+ # Filter: Shared
243
+ if "общий" in desc or "shared" in desc or "обще" in desc: continue
244
+
245
+ # Filter: Plus/Business
246
+ has_plus = "plus" in desc or "плюс" in desc
247
+ has_business = "business" in desc or "бизнес" in desc
248
+ has_month = "month" in desc or "месяц" in desc or "30" in desc
249
+
250
+ if not ((has_plus or has_business) and has_month):
251
+ continue
252
+
253
+ # Get Price
254
+ price_div = offer.find("div", class_="tc-price")
255
+ if not price_div: continue
256
+ raw_s = price_div.get("data-s")
257
+ if not raw_s: continue
258
+ price = float(raw_s)
259
+
260
+ user_div = offer.find("div", class_="media-user-name")
261
+ seller = user_div.get_text(strip=True) if user_div else "Unknown"
262
+
263
+ if seller == MY_USERNAME:
264
+ if my_current_price is None or price < my_current_price:
265
+ my_current_price = price
266
+ else:
267
+ online_competitor_prices.append(price)
268
+
269
+ logger.info(f"Price Check Done. My Price: {my_current_price}, Min Competitor: {min(online_competitor_prices) if online_competitor_prices else 'None'}")
270
+
271
+ if online_competitor_prices and my_current_price:
272
+ min_competitor = min(online_competitor_prices)
273
+ target_price = round(max(1.0, min_competitor - 0.01), 2)
274
+
275
+ if my_current_price > target_price:
276
+ send_telegram_notification(
277
+ f"📉 <b>Конкурент дешевле!</b>\n\n"
278
+ f"👤 Мин. конкурент: {min_competitor:.2f} ₽\n"
279
+ f"💰 Твоя цена: {my_current_price:.2f} ₽\n"
280
+ f"👉 <b>Рекомендую: {target_price:.2f} ₽</b>"
281
+ )
282
+ elif not my_current_price:
283
+ logger.warning(f"User {MY_USERNAME} not found on page!")
284
+
285
+ except Exception as e:
286
+ logger.error(f"Dump check failed: {e}")
287
+
288
+ time.sleep(wait_time)
289
+
290
+ def main():
291
+ global GOLDEN_KEY, MY_USERNAME
292
+ logger.info("Starting FunPay Bot Loop on Hugging Face (DEBUG MODE)...")
293
+
294
+ # Start Aux Threads
295
+ t_heartbeat = threading.Thread(target=send_heartbeat, daemon=True)
296
+ t_heartbeat.start()
297
+
298
+ t_dump = threading.Thread(target=dump_price_check, daemon=True)
299
+ t_dump.start()
300
+
301
+ remote_key = get_golden_key_from_api()
302
+ if remote_key:
303
+ logger.info(f"✅ Key fetched from API: {remote_key[:5]}...")
304
+ GOLDEN_KEY = remote_key
305
+ else:
306
+ logger.error("❌ Failed to fetch key from API!")
307
+
308
+ while True:
309
+ try:
310
+ try:
311
+ logger.info(f"Attempting login with key ending in ...{GOLDEN_KEY[-5:]}")
312
+ acc = Account(GOLDEN_KEY).get()
313
+ runner = Runner(acc)
314
+ MY_USERNAME = acc.username
315
+ logger.info(f"Logged in successfully: {acc.username}")
316
+ send_telegram_notification(f"🤖 <b>FunPay Bot Started!</b>\nUser: {acc.username}\nMonitoring: Active (Smart Filter)")
317
+ except Exception as e:
318
+ logger.error(f"Login failed: {e}")
319
+ logger.error(traceback.format_exc()) # Print full traceback
320
+
321
+ time.sleep(5)
322
+ new_key = get_golden_key_from_api()
323
+ if new_key and new_key != GOLDEN_KEY:
324
+ GOLDEN_KEY = new_key
325
+ continue
326
+ send_telegram_notification(f"⚠️ <b>Login Failed!</b>\n{e}")
327
+ time.sleep(60)
328
+ continue
329
+
330
+ for event in runner.listen(requests_delay=4):
331
+ if event.type == enums.EventTypes.NEW_ORDER:
332
+ order = event.order
333
+ logger.info(f"New Order Event: {order.id}")
334
+ send_telegram_notification(f"💰 Новый заказ!\n{order.buyer_username} - {order.sum} руб.")
335
+ product = get_product_from_api(1, order.buyer_username, order.sum)
336
+ if product:
337
+ try:
338
+ chat = acc.get_chat_by_name(order.buyer_username, True)
339
+ acc.send_message(chat.id, f"Спасибо за покупку!\n\n{product}")
340
+ send_telegram_notification(f"✅ Выдан товар для {order.id}")
341
+ except Exception as e:
342
+ logger.error(f"Chat error: {e}")
343
+ send_telegram_notification(f"⚠️ Ошибка чата: {e}")
344
+ else:
345
+ logger.error(f"Product not found for order {order.id}")
346
+ send_telegram_notification(f"❌ Ошибка выдачи для {order.id}")
347
+
348
+ except Exception as e:
349
+ logger.error(f"Main Loop error: {e}")
350
+ logger.error(traceback.format_exc())
351
+ time.sleep(30)
352
+
353
+ if __name__ == '__main__':
354
+ main()
355
+