two / funpay_loop.py
VLADIMIROFF777's picture
Create funpay_loop.py
adae9d7 verified
import threading
import time
import requests
import json
import random
import logging
import sys
import traceback
from flask import Flask, jsonify, make_response
from bs4 import BeautifulSoup
from FunPayAPI.account import Account
from FunPayAPI.common import enums
try:
from FunPayAPI.updater.runner import Runner
except ImportError:
from FunPayAPI.account import Runner
# ==========================================
# 0. DEBUGGING INFRASTRUCTURE
# ==========================================
LOG_BUFFER = []
class BufferHandler(logging.Handler):
def emit(self, record):
try:
msg = self.format(record)
LOG_BUFFER.append(f"{time.strftime('%H:%M:%S', time.localtime())} - {msg}")
if len(LOG_BUFFER) > 200:
LOG_BUFFER.pop(0)
except Exception:
pass
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("FunPayBot")
logger.addHandler(BufferHandler())
# ==========================================
# 1. KEEP-ALIVE SERVER (Hugging Face)
# ==========================================
app = Flask(__name__)
@app.route('/')
def home():
lines = "\n".join(LOG_BUFFER)
# Return HTML for better display
return f"""
<h1>🤖 Bot Status: Running</h1>
<p>Go to <a href="/logs">/logs</a> to see full logs.</p>
<p>Go to <a href="/test_tg">/test_tg</a> to force test Telegram.</p>
<h3>Last 10 Logs:</h3>
<pre>{lines[-1000:]}</pre>
"""
@app.route('/logs')
def get_logs():
return "<pre>" + "\n".join(LOG_BUFFER) + "</pre>"
@app.route('/test_tg')
def test_tg():
try:
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
payload = {"chat_id": TELEGRAM_CHAT_ID, "text": "TEST MESSAGE FROM WEB. If you see this, connectivity works."}
r = requests.post(url, json=payload, timeout=5)
return f"OK: {r.status_code} {r.text}"
except Exception as e:
return f"ERROR: {e}"
def run_web():
logger.info("Keep-Alive Web Server started on port 7860")
app.run(host='0.0.0.0', port=7860)
t = threading.Thread(target=run_web)
t.start()
# ==========================================
# 2. PROXY MONKEY PATCH (Cloudflare)
# ==========================================
PROXY_URL = "https://damp-cell-4702.newnout6.workers.dev/"
original_request = requests.Session.request
def proxy_request(self, method, url, *args, **kwargs):
if "funpay.com" in url and PROXY_URL not in url:
target = url
valid_cookies = self.cookies.get_dict(domain=".funpay.com")
valid_cookies.update(self.cookies.get_dict(domain="funpay.com"))
kw_cookies = kwargs.get('cookies')
if kw_cookies: valid_cookies.update(kw_cookies)
headers = kwargs.get('headers', {}) or {}
headers = headers.copy()
found_cookie_key = None
for k in list(headers.keys()):
if k.lower() == 'cookie':
found_cookie_key = k
raw_c = headers[k]
for part in raw_c.split(';'):
if '=' in part:
kv = part.strip().split('=', 1)
if len(kv) == 2:
valid_cookies[kv[0]] = kv[1]
del headers[k]
cookie_header = "; ".join([f"{k}={v}" for k, v in valid_cookies.items()])
if cookie_header: headers['Cookie'] = cookie_header
if 'User-Agent' not in headers:
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"
# Ensure RUB/RU locale
if 'cy' not in valid_cookies: valid_cookies['cy'] = 'rub'
if 'locale' not in valid_cookies: valid_cookies['locale'] = 'ru'
params = kwargs.get('params', {}) or {}
params = params.copy()
params['url'] = target
kwargs['headers'] = headers
kwargs['params'] = params
kwargs['cookies'] = {}
return original_request(self, method, PROXY_URL, *args, **kwargs)
return original_request(self, method, url, *args, **kwargs)
requests.Session.request = proxy_request
requests.request = proxy_request
# ==========================================
# 3. BOT LOGIC
# ==========================================
TELEGRAM_BOT_TOKEN = "8203118488:AAGC1EQavBt0u9suYQ32qt4owkU_VuubWG0"
TELEGRAM_CHAT_ID = "1517760699"
API_URL = "http://shrvld789.pythonanywhere.com"
# Default Key
GOLDEN_KEY = "dummy_key_waiting_for_update"
DUMP_CATEGORY_URL = "https://funpay.com/lots/1355/"
MY_USERNAME = "" # Will be filled after login
def send_telegram_notification(message):
try:
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
payload = {"chat_id": TELEGRAM_CHAT_ID, "text": message, "parse_mode": "HTML"}
logger.info(f"Adding to TG queue: {message[:20]}...")
requests.post(url, json=payload, timeout=10)
except Exception as e:
logger.error(f"Failed to send Telegram notification: {e}")
def get_golden_key_from_api():
try:
url = f"{API_URL}/api/config"
resp = requests.get(url, timeout=10)
if resp.status_code == 200:
config = resp.json()
key = config.get('golden_key')
if key and len(key) > 10:
return key
except Exception as e:
logger.error(f"Failed to fetch key from API: {e}")
return None
def get_product_from_api(quantity, buyer_name, price):
try:
url = f"{API_URL}/api/sell"
payload = {"quantity": quantity, "customer_name": buyer_name, "price": price}
response = requests.post(url, json=payload, timeout=20)
if response.status_code == 200:
data = response.json()
if data.get('success'):
return "\n".join(data.get('accounts', []))
return None
except Exception as e:
logger.error(f"Error requesting product from API: {e}")
return None
def send_heartbeat():
"""Periodically sends a heartbeat to the Admin Bot"""
while True:
try:
requests.post(f"{API_URL}/api/heartbeat", timeout=5)
except Exception as e:
logger.error(f"Heartbeat failed: {e}")
time.sleep(60)
def dump_price_check():
"""Monitors competitor prices and suggests updates"""
global MY_USERNAME
logger.info("Initializing Price Monitor...")
# Wait for username with attempts
attempts = 0
while not MY_USERNAME:
attempts += 1
if attempts % 10 == 0:
logger.warning("Still waiting for MY_USERNAME...")
time.sleep(5)
logger.info(f"Starting Price Monitor for {MY_USERNAME}...")
while True:
try:
wait_time = random.randint(300, 420)
logger.info(f"Checking prices... Next check in {wait_time}s")
# Manually fetch page
session = requests.Session()
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",
"Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"
}
resp = session.get(DUMP_CATEGORY_URL, headers=headers, cookies={'cy': 'rub', 'locale': 'ru'}, timeout=20)
if resp.status_code == 200:
soup = BeautifulSoup(resp.text, "html.parser")
offers = soup.find_all("a", class_="tc-item")
online_competitor_prices = []
my_current_price = None
for offer in offers:
# Filter 1: Check Online Status
if offer.get("data-online") != "1":
continue
# Filter 2: Check Subscription (Must include "с подпиской")
subscription = offer.get("data-f-subscription", "").lower()
if "подписк" not in subscription or "без" in subscription:
continue
# Filter 3: Check Keywords in Description
desc_div = offer.find("div", class_="tc-desc-text")
desc = desc_div.get_text(strip=True).lower() if desc_div else ""
# Filter: Reviews
rating_div = offer.find("span", class_="rating-mini-count")
if rating_div:
try:
reviews = int(rating_div.get_text(strip=True).replace(' ', ''))
if reviews > 100: continue
except: pass
# Filter: Shared
if "общий" in desc or "shared" in desc or "обще" in desc: continue
# Filter: Plus/Business
has_plus = "plus" in desc or "плюс" in desc
has_business = "business" in desc or "бизнес" in desc
has_month = "month" in desc or "месяц" in desc or "30" in desc
if not ((has_plus or has_business) and has_month):
continue
# Get Price
price_div = offer.find("div", class_="tc-price")
if not price_div: continue
raw_s = price_div.get("data-s")
if not raw_s: continue
price = float(raw_s)
user_div = offer.find("div", class_="media-user-name")
seller = user_div.get_text(strip=True) if user_div else "Unknown"
if seller == MY_USERNAME:
if my_current_price is None or price < my_current_price:
my_current_price = price
else:
online_competitor_prices.append(price)
logger.info(f"Price Check Done. My Price: {my_current_price}, Min Competitor: {min(online_competitor_prices) if online_competitor_prices else 'None'}")
if online_competitor_prices and my_current_price:
min_competitor = min(online_competitor_prices)
target_price = round(max(1.0, min_competitor - 0.01), 2)
if my_current_price > target_price:
send_telegram_notification(
f"📉 <b>Конкурент дешевле!</b>\n\n"
f"👤 Мин. конкурент: {min_competitor:.2f} ₽\n"
f"💰 Твоя цена: {my_current_price:.2f} ₽\n"
f"👉 <b>Рекомендую: {target_price:.2f} ₽</b>"
)
elif not my_current_price:
logger.warning(f"User {MY_USERNAME} not found on page!")
except Exception as e:
logger.error(f"Dump check failed: {e}")
time.sleep(wait_time)
def main():
global GOLDEN_KEY, MY_USERNAME
logger.info("Starting FunPay Bot Loop on Hugging Face (DEBUG MODE)...")
# Start Aux Threads
t_heartbeat = threading.Thread(target=send_heartbeat, daemon=True)
t_heartbeat.start()
t_dump = threading.Thread(target=dump_price_check, daemon=True)
t_dump.start()
remote_key = get_golden_key_from_api()
if remote_key:
logger.info(f"✅ Key fetched from API: {remote_key[:5]}...")
GOLDEN_KEY = remote_key
else:
logger.error("❌ Failed to fetch key from API!")
while True:
try:
try:
logger.info(f"Attempting login with key ending in ...{GOLDEN_KEY[-5:]}")
acc = Account(GOLDEN_KEY).get()
runner = Runner(acc)
MY_USERNAME = acc.username
logger.info(f"Logged in successfully: {acc.username}")
send_telegram_notification(f"🤖 <b>FunPay Bot Started!</b>\nUser: {acc.username}\nMonitoring: Active (Smart Filter)")
except Exception as e:
logger.error(f"Login failed: {e}")
logger.error(traceback.format_exc()) # Print full traceback
time.sleep(5)
new_key = get_golden_key_from_api()
if new_key and new_key != GOLDEN_KEY:
GOLDEN_KEY = new_key
continue
send_telegram_notification(f"⚠️ <b>Login Failed!</b>\n{e}")
time.sleep(60)
continue
for event in runner.listen(requests_delay=4):
if event.type == enums.EventTypes.NEW_ORDER:
order = event.order
logger.info(f"New Order Event: {order.id}")
send_telegram_notification(f"💰 Новый заказ!\n{order.buyer_username} - {order.sum} руб.")
product = get_product_from_api(1, order.buyer_username, order.sum)
if product:
try:
chat = acc.get_chat_by_name(order.buyer_username, True)
acc.send_message(chat.id, f"Спасибо за покупку!\n\n{product}")
send_telegram_notification(f"✅ Выдан товар для {order.id}")
except Exception as e:
logger.error(f"Chat error: {e}")
send_telegram_notification(f"⚠️ Ошибка чата: {e}")
else:
logger.error(f"Product not found for order {order.id}")
send_telegram_notification(f"❌ Ошибка выдачи для {order.id}")
except Exception as e:
logger.error(f"Main Loop error: {e}")
logger.error(traceback.format_exc())
time.sleep(30)
if __name__ == '__main__':
main()