import telebot import requests import time import threading import json import os import io
try: import openpyxl from openpyxl.styles import Font, PatternFill, Alignment EXCEL_AVAILABLE = True except ImportError: EXCEL_AVAILABLE = False
import os from dotenv import load_dotenv load_dotenv()
BOT_TOKEN = os.getenv("8625434679:AAG9ZV61dgqBrVc_d-fNvpd0jSAk0pduSUA") API_KEY = os.getenv("API_KEY") BASE_URL = os.getenv("BASE_URL", "") HEADERS = {"X-API-Key": API_KEY} ADMIN_IDS = [int(x) for x in os.getenv("ADMIN_IDS", "").split(",") if x.strip()] STATS_FILE = "stats.json" HISTORY_FILE = "history.json" MIRRORS_FILE = "mirrors.json" PHONES_FILE = "phones.json" CRYPTO_BOT_TOKEN = os.getenv("CRYPTO_BOT_TOKEN") CRYPTO_BOT_API = "https://pay.crypt.bot/api" CHANNEL_URL = os.getenv("CHANNEL_URL", "") CHANNEL_USERNAME = os.getenv("CHANNEL_USERNAME", "") MIRROR_AUTOPAYOUT_MIN_RUB = float(os.getenv("MIRROR_AUTOPAYOUT_MIN_RUB", "1.0"))
SERVICE_PRICES = { "Сбер ID": 0.03, "ITMS - табачка": 0.13, "Юрент": 0.03, "Яндекс [NEW]": 0.06, "Яндекс [#1]": 0.06, "Яндекс Еда": 0.08, "Яндекс Еда [#3]": 0.08, "Яндекс Еда [#1]": 0.08, "Яндекс Еда [новое]": 0.15, "Теле2": 0.03, "Вкусно и точка": 0.13, "Яндекс Пей": 0.05, "Пятерочка": 0.04, "Пятерочка [#2]": 0.04, "Магнит": 0.04, "Ростикс": 0.04, "ОЗОН": 0.09, "Whoosh": 0.0, "ITMS": 0.13, "Яндекс Еда [#2]": 0.13, }
WHOOSH_API_URL = os.getenv("WHOOSH_API_URL", "") WHOOSH_API_KEY = os.getenv("WHOOSH_API_KEY", "") WHOOSH_HEADERS = {"X-API-Key": WHOOSH_API_KEY, "Content-Type": "application/json"}
bot = telebot.TeleBot(BOT_TOKEN) user_state = {} payout_in_progress = set() # UID воркеров у которых сейчас идёт выплата mirror_bots = {} # token -> TeleBot instance mirror_user_states = {} # token -> {uid: state}
def save_phone_record(uid, username, phone, bot_name, service_name, success=True): """Сохраняем запись о сданном номере""" try: if os.path.exists(PHONES_FILE): with open(PHONES_FILE, "r", encoding="utf-8") as f: phones = json.load(f) else: phones = [] phones.append({ "uid": uid, "username": username, "phone": phone, "bot": bot_name, "service": service_name, "success": success, "ts": int(time.time()), "date": time.strftime("%d.%m.%Y %H:%M") }) with open(PHONES_FILE, "w", encoding="utf-8") as f: json.dump(phones, f, ensure_ascii=False) except Exception as e: print(f"Ошибка записи номера: {e}")
def load_stats(): if os.path.exists(STATS_FILE): with open(STATS_FILE, "r", encoding="utf-8") as f: return json.load(f) return {}
def save_stats(stats): with open(STATS_FILE, "w", encoding="utf-8") as f: json.dump(stats, f, ensure_ascii=False)
def add_stat(uid, username, success=True, service_name=None, earned_rub=0, mirror_token=None): stats = load_stats() key = str(uid) now_ts = int(time.time()) if key not in stats: stats[key] = {"username": username or str(uid), "success": 0, "failed": 0, "services": {}, "earned_rub": 0, "paid_rub": 0, "history_24h": []} stats[key]["username"] = username or stats[key].get("username") or str(uid) if "services" not in stats[key]: stats[key]["services"] = {} if "history_24h" not in stats[key]: stats[key]["history_24h"] = [] # запись в историю 24ч stats[key]["history_24h"].append({"ts": now_ts, "success": success, "service": service_name, "earned": earned_rub}) cutoff = now_ts - 86400 stats[key]["history_24h"] = [e for e in stats[key]["history_24h"] if e.get("ts", 0) >= cutoff] if success: stats[key]["success"] += 1 stats[key]["earned_rub"] = stats[key].get("earned_rub", 0) + earned_rub if service_name: stats[key]["services"][service_name] = stats[key]["services"].get(service_name, 0) + 1 else: stats[key]["failed"] += 1 save_stats(stats) if mirror_token: _add_mirror_worker_stat(mirror_token, uid, username, success, service_name, earned_rub)
def get_stats_24h(uid): stats = load_stats() s = stats.get(str(uid), {}) cutoff = int(time.time()) - 86400 recent = [e for e in s.get("history_24h", []) if e.get("ts", 0) >= cutoff] ok = sum(1 for e in recent if e.get("success")) fail = sum(1 for e in recent if not e.get("success")) earned = sum(e.get("earned", 0) for e in recent if e.get("success")) return ok, fail, earned
def reset_stat(uid): stats = load_stats() key = str(uid) if key in stats: stats[key]["success"] = 0 stats[key]["failed"] = 0 stats[key]["services"] = {} stats[key]["earned_rub"] = 0 save_stats(stats) return True return False
def add_payout_history(uid, username, amount_rub, amount_usdt, breakdown): history = [] if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, "r", encoding="utf-8") as f: history = json.load(f) history.append({ "uid": str(uid), "username": username, "amount_rub": amount_rub, "amount_usdt": amount_usdt, "breakdown": breakdown, "date": time.strftime("%d.%m.%Y %H:%M") }) with open(HISTORY_FILE, "w", encoding="utf-8") as f: json.dump(history, f, ensure_ascii=False)
def get_payout_history(): if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, "r", encoding="utf-8") as f: return json.load(f) return []
def notify_admins_payout(uid, username, amount_rub, amount_usdt, source="основной бот"): text = (f"💸 Запрос на вывод\n\n" f"👤 @{username} (ID: {uid})\n" f"💵 {amount_usdt} USDT (≈{amount_rub}$)\n" f"🤖 Источник: {source}\n" f"🕐 {time.strftime('%d.%m.%Y %H:%M')}") for admin_id in ADMIN_IDS: try: bot.send_message(admin_id, text) except Exception: pass
def load_mirrors(): if os.path.exists(MIRRORS_FILE): with open(MIRRORS_FILE, "r", encoding="utf-8") as f: return json.load(f) return {}
def save_mirrors(mirrors): with open(MIRRORS_FILE, "w", encoding="utf-8") as f: json.dump(mirrors, f, ensure_ascii=False, indent=2)
def get_user_mirrors(uid): mirrors = load_mirrors() return {k: v for k, v in mirrors.items() if v.get("owner_uid") == str(uid)}
def get_mirror_by_token(token): return load_mirrors().get(token)
def create_mirror(owner_uid, owner_username, bot_token, bot_username, prices): mirrors = load_mirrors() mirrors[bot_token] = { "owner_uid": str(owner_uid), "owner_username": owner_username, "bot_token": bot_token, "bot_username": bot_username, "prices": prices, "created_at": time.strftime("%d.%m.%Y %H:%M"), "active": True, "workers": {}, "owner_earned_rub": 0, "owner_paid_rub": 0, "autopayout_enabled": True, "autopayout_min_rub": MIRROR_AUTOPAYOUT_MIN_RUB, } save_mirrors(mirrors) return mirrors[bot_token]
def _add_mirror_worker_stat(mirror_token, uid, username, success, service_name, earned_rub): mirrors = load_mirrors() if mirror_token not in mirrors: return key = str(uid) w = mirrors[mirror_token].setdefault("workers", {}) now_ts = int(time.time()) if key not in w: w[key] = {"username": username or str(uid), "success": 0, "failed": 0, "services": {}, "earned_rub": 0, "history_24h": [], "joined_at": time.strftime("%d.%m.%Y %H:%M")} w[key]["username"] = username or w[key].get("username") or str(uid) w[key].setdefault("history_24h", []).append( {"ts": now_ts, "success": success, "service": service_name, "earned": earned_rub}) cutoff = now_ts - 86400 w[key]["history_24h"] = [e for e in w[key]["history_24h"] if e.get("ts", 0) >= cutoff] if success: w[key]["success"] += 1 w[key]["earned_rub"] = w[key].get("earned_rub", 0) + earned_rub if service_name: w[key]["services"][service_name] = w[key]["services"].get(service_name, 0) + 1 # прибыль владельца зеркала = базовая цена - цена зеркала base_price = SERVICE_PRICES.get(service_name, 0) mirror_price = mirrors[mirror_token].get("prices", {}).get(service_name, 0) owner_profit = base_price - mirror_price if owner_profit > 0: mirrors[mirror_token]["owner_earned_rub"] = mirrors[mirror_token].get("owner_earned_rub", 0) + owner_profit else: w[key]["failed"] += 1 save_mirrors(mirrors) _check_mirror_autopayout(mirror_token)
def _check_mirror_autopayout(mirror_token): mirrors = load_mirrors() m = mirrors.get(mirror_token) if not m or not m.get("autopayout_enabled", True): return threshold = m.get("autopayout_min_rub", MIRROR_AUTOPAYOUT_MIN_RUB) balance = m.get("owner_earned_rub", 0) - m.get("owner_paid_rub", 0) if balance < threshold: return owner_uid = int(m["owner_uid"]) amount_usdt = round(balance, 2) if amount_usdt < 0.01: return result, error = create_check(amount_usdt, owner_uid) if result: check_url = result.get("bot_check_url", "") mirrors = load_mirrors() mirrors[mirror_token]["owner_paid_rub"] = mirrors[mirror_token].get("owner_paid_rub", 0) + balance save_mirrors(mirrors) try: bot.send_message(owner_uid, f"💰 Автовыплата дохода зеркала\n\n" f"🤖 @{m.get('bot_username','?')}\n" f"💵 {amount_usdt} USDT\n\n{check_url}") except Exception: pass notify_admins_payout(owner_uid, m.get("owner_username", str(owner_uid)), balance, amount_usdt, f"автовыплата зеркала @{m.get('bot_username','?')}")
def format_stats_text(s, name=None): total = s["success"] + s["failed"] lines = [] if name: lines.append(f"👤 {name}\n") lines.append(f"✅ Успешных: {s['success']}") services = s.get("services", {}) if services: sorted_services = sorted(services.items(), key=lambda x: x[1], reverse=True) for sname, count in sorted_services: lines.append(f" • {sname} — {count}") lines.append(f"❌ Неудачных: {s['failed']}") lines.append(f"📦 Всего: {total}") return "\n".join(lines)
def calc_payout(services_dict): """Считаем сумму выплаты в рублях""" total = 0 breakdown = [] for sname, count in services_dict.items(): price = SERVICE_PRICES.get(sname, 0) if price > 0 and count > 0: amount = price * count total += amount breakdown.append(f"• {sname}: {count} × {price}$ = {amount}$") return total, breakdown
def get_usdt_rate(): """Цены уже в USD, USDT ≈ USD, возвращаем 1""" return 1
def get_app_balance(): """Получаем баланс приложения""" try: r = requests.get( f"{CRYPTO_BOT_API}/getBalance", headers={"Crypto-Pay-API-Token": CRYPTO_BOT_TOKEN}, timeout=10 ) if r.status_code == 200: data = r.json() if data.get("ok"): for b in data["result"]: if b["currency_code"] == "USDT": return float(b["available"]) except Exception as e: print(f"Ошибка получения баланса: {e}") return None
def create_check(amount_usdt, uid=None): """Создаём чек (без привязки к пользователю — может активировать любой)""" try: payload = { "asset": "USDT", "amount": str(round(amount_usdt, 2)), } r = requests.post( f"{CRYPTO_BOT_API}/createCheck", headers={"Crypto-Pay-API-Token": CRYPTO_BOT_TOKEN}, json=payload, timeout=10 ) if r.status_code == 200: data = r.json() if data.get("ok"): return data["result"], None return None, data.get("error", {}).get("name", "Ошибка") return None, f"HTTP {r.status_code}: {r.text[:200]}" except Exception as e: return None, str(e)
def whoosh_send_sms(phone): """Отправить SMS через Whoosh API""" try: r = requests.post( f"{WHOOSH_API_URL}/api/sms/send", headers=WHOOSH_HEADERS, json={"phone": phone}, timeout=15 ) data = r.json() if r.status_code == 200 and data.get("ok"): return True, data.get("confirm_type", "SMS") if r.status_code == 409: return False, "Номер уже сдан ранее" return False, data.get("error", f"Ошибка {r.status_code}") except Exception as e: return False, str(e)
def whoosh_submit_number(phone, code): """Сдать номер + код через Whoosh API""" try: r = requests.post( f"{WHOOSH_API_URL}/api/number/submit", headers=WHOOSH_HEADERS, json={"phone": phone, "code": code}, timeout=15 ) data = r.json() if r.status_code == 200 and data.get("ok") and data.get("valid"): return True, data.get("credited", 0) if r.status_code == 409: return False, "Номер уже сдан ранее" reason = data.get("reason", data.get("error", "Отклонён")) return False, reason except Exception as e: return False, str(e)
def get_services(): services = [] try: r = requests.get(f"{BASE_URL}/worker-api/services", headers=HEADERS, timeout=10) if r.status_code == 200: services = r.json() except Exception as e: print(f"Ошибка получения сервисов: {e}") # Добавляем Whoosh как отдельный сервис services.append({"id": "whoosh_service", "name": "Whoosh"}) return services
def create_activations(phone, service_ids): try: r = requests.post( f"{BASE_URL}/worker-api/activations", headers=HEADERS, json={"phone": phone, "service_ids": service_ids}, timeout=10 ) if r.status_code == 201: return r.json(), None else: detail = r.json().get("detail", "Неизвестная ошибка") return None, detail except Exception as e: return None, str(e)
def get_activation(act_id): try: r = requests.get(f"{BASE_URL}/worker-api/activations/{act_id}", headers=HEADERS, timeout=10) if r.status_code == 200: return r.json() except: pass return None
def send_sms_code(act_id, code): try: r = requests.post( f"{BASE_URL}/worker-api/activations/{act_id}/sms", headers=HEADERS, json={"code": code}, timeout=10 ) if r.status_code == 200: return True, None else: detail = r.json().get("detail", "Неизвестная ошибка") return False, detail except Exception as e: return False, str(e)
def cancel_activation(act_id): try: requests.post(f"{BASE_URL}/worker-api/activations/{act_id}/cancel", headers=HEADERS, timeout=10) except: pass
def main_menu(): markup = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True) markup.add("📲 Новая активация") markup.row("📊 Моя статистика", "💸 Вывести") markup.row("🔗 Создать зеркало", "📢 Канал") markup.add("❌ Отмена") return markup
def admin_menu(): markup = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True) markup.add("📲 Новая активация") markup.row("📊 Моя статистика", "👥 Воркеры") markup.row("📈 Полная статистика", "🏆 Топ воркеров") markup.row("💸 Вывести", "📥 Экспорт CSV") markup.row("💰 Пополнить", "💼 Баланс") markup.row("📜 История выплат", "🔍 Все зеркала") markup.row("💵 Цены", "📣 Рассылка") markup.row("🔗 Создать зеркало", "📢 Канал") markup.add("❌ Отмена") return markup
def services_keyboard(services, selected_ids, custom_prices=None): markup = telebot.types.InlineKeyboardMarkup() for s in services: checked = "✅ " if s["id"] in selected_ids else "☐ " # Показываем цену рядом с названием if custom_prices: price = custom_prices.get(s['name'], 0) else: price = SERVICE_PRICES.get(s['name'], 0) markup.add(telebot.types.InlineKeyboardButton( text=f"{checked}{s['name']} — {price}$", callback_data=f"tog_{s['id']}" )) if selected_ids: markup.add(telebot.types.InlineKeyboardButton( text=f"➡️ Продолжить ({len(selected_ids)} выбрано)", callback_data="confirm_services" )) return markup
def workers_list_keyboard(): stats = load_stats() markup = telebot.types.InlineKeyboardMarkup() if not stats: return markup for uid, s in stats.items(): total = s["success"] + s["failed"] markup.add(telebot.types.InlineKeyboardButton( text=f"👤 {s['username']} | ✅{s['success']} ❌{s['failed']} 📦{total}", callback_data=f"worker_{uid}" )) return markup
def worker_detail_keyboard(uid): markup = telebot.types.InlineKeyboardMarkup() markup.add(telebot.types.InlineKeyboardButton( text="🔄 Сбросить статистику", callback_data=f"doreset_{uid}" )) markup.add(telebot.types.InlineKeyboardButton( text="◀️ Назад к списку", callback_data="back_workers" )) return markup
@bot.message_handler(commands=['start']) def start(message): uid = message.from_user.id username = message.from_user.username or message.from_user.first_name or str(uid) user_state.pop(uid, None) stats = load_stats() if str(uid) not in stats: stats[str(uid)] = {"username": username, "success": 0, "failed": 0, "services": {}} save_stats(stats) else: stats[str(uid)]["username"] = username if "services" not in stats[str(uid)]: stats[str(uid)]["services"] = {} save_stats(stats) menu = admin_menu() if uid in ADMIN_IDS else main_menu() bot.send_message( message.chat.id, "👋 Привет! Я бот для регистрации через SMS.\n\nНажми кнопку ниже чтобы начать:", reply_markup=menu )
@bot.message_handler(func=lambda m: m.text == "📊 Моя статистика") def my_stats(message): uid = message.from_user.id stats = load_stats() s = stats.get(str(uid), {"success": 0, "failed": 0, "services": {}, "earned_rub": 0}) ok24, fail24, earned24 = get_stats_24h(uid) rate = get_usdt_rate() total_earned = s.get("earned_rub", 0) total_success = s.get("success", 0) total_failed = s.get("failed", 0) services_text = "" services = s.get("services", {}) if services: sorted_services = sorted(services.items(), key=lambda x: x[1], reverse=True) svc_lines = [f" • {sname} — {count}" for sname, count in sorted_services] services_text = "\n" + "\n".join(svc_lines) text = (f"📊 Твоя статистика\n\n" f"📅 За 24 часа:\n" f" ✅ Успешных: {ok24}\n" f" ❌ Неудачных: {fail24}\n" f" 💵 Заработано: {earned24}$\n\n" f"📆 За всё время:\n" f" ✅ Успешных: {total_success}{services_text}\n" f" ❌ Неудачных: {total_failed}\n" f" 📦 Всего: {total_success + total_failed}\n" f" 💵 Заработано: {total_earned}$") bot.send_message(message.chat.id, text)
@bot.message_handler(func=lambda m: m.text == "📈 Полная статистика" and m.from_user.id in ADMIN_IDS) def admin_full_stats(message): stats = load_stats() mirrors = load_mirrors() rate = get_usdt_rate() cutoff = int(time.time()) - 86400 bot_total_success = 0 bot_total_failed = 0 bot_earned_alltime = 0 bot_earned_24h = 0 bot_success_24h = 0 bot_workers_total = len(stats) for uid, s in stats.items(): bot_total_success += s.get("success", 0) bot_total_failed += s.get("failed", 0) bot_earned_alltime += s.get("earned_rub", 0) for e in s.get("history_24h", []): if e.get("ts", 0) >= cutoff: if e.get("success"): bot_earned_24h += e.get("earned", 0) bot_success_24h += 1 total_mirrors = len(mirrors) total_mirror_workers = 0 mirror_earned_alltime = 0 mirror_earned_24h = 0 mirror_success_24h = 0 mirror_total_success = 0 mirror_total_failed = 0 for token, m in mirrors.items(): workers = m.get("workers", {}) total_mirror_workers += len(workers) mirror_earned_alltime += m.get("owner_earned_rub", 0) for wid, w in workers.items(): mirror_total_success += w.get("success", 0) mirror_total_failed += w.get("failed", 0) for e in w.get("history_24h", []): if e.get("ts", 0) >= cutoff: if e.get("success"): mirror_earned_24h += e.get("earned", 0) mirror_success_24h += 1 grand_earned_alltime = bot_earned_alltime + mirror_earned_alltime grand_earned_24h = bot_earned_24h + mirror_earned_24h grand_success = bot_total_success + mirror_total_success text = (f"📈 ПОЛНАЯ СТАТИСТИКА\n" f"{'═' * 25}\n\n" f"🤖 ОСНОВНОЙ БОТ\n" f" 👥 Воркеров: {bot_workers_total}\n" f" 📅 За 24ч: ✅ {bot_success_24h} | 💵 {bot_earned_24h}$\n" f" 📆 Всё время: ✅ {bot_total_success} ❌ {bot_total_failed}\n" f" 💰 Заработано: {bot_earned_alltime}$\n\n" f"🪞 ЗЕРКАЛА (всего: {total_mirrors})\n" f" 👥 Воркеров: {total_mirror_workers}\n" f" 📅 За 24ч: ✅ {mirror_success_24h} | 💵 {mirror_earned_24h}$\n" f" 📆 Всё время: ✅ {mirror_total_success} ❌ {mirror_total_failed}\n" f" 💰 Доход владельцев: {mirror_earned_alltime}$\n\n" f"{'═' * 25}\n" f"💎 ИТОГО\n" f" 📅 За 24ч: 💵 {grand_earned_24h}$\n" f" 📆 Всё время: 💵 {grand_earned_alltime}$\n" f" ✅ Всего активаций: {grand_success}") markup = telebot.types.InlineKeyboardMarkup() for token, m in mirrors.items(): bname = m.get("bot_username", "?") workers = m.get("workers", {}) total_act = sum(w.get("success", 0) for w in workers.values()) prefix = token.split(":")[0] markup.add(telebot.types.InlineKeyboardButton( f"🤖 @{bname} | 👥{len(workers)} | ✅{total_act}", callback_data=f"mstat_{prefix}")) bot.send_message(message.chat.id, text, reply_markup=markup)
@bot.callback_query_handler(func=lambda call: call.data.startswith("mstat_")) def admin_mirror_detail(call): try: bot.answer_callback_query(call.id) except Exception: pass prefix = call.data[6:] mirrors = load_mirrors() rate = get_usdt_rate() cutoff = int(time.time()) - 86400 mirror_data = None for token, m in mirrors.items(): if token.startswith(prefix): mirror_data = m break if not mirror_data: bot.send_message(call.message.chat.id, "❌ Зеркало не найдено.") return bname = mirror_data.get("bot_username", "?") owner_uid = mirror_data.get("owner_uid", "?") owner_username = mirror_data.get("owner_username", "?") workers = mirror_data.get("workers", {}) earned_alltime = mirror_data.get("owner_earned_rub", 0) paid = mirror_data.get("owner_paid_rub", 0) balance = earned_alltime - paid created = mirror_data.get("created_at", "?") ap = "ВКЛ ✅" if mirror_data.get("autopayout_enabled", True) else "ВЫКЛ ❌" threshold = mirror_data.get("autopayout_min_rub", MIRROR_AUTOPAYOUT_MIN_RUB) prices = mirror_data.get("prices", {}) prices_text = "\n".join([f" • {k}: {v}$" for k, v in prices.items() if v > 0]) total_success = 0 total_failed = 0 success_24h = 0 earned_24h = 0 worker_lines = [] for wid, w in workers.items(): w_success = w.get("success", 0) w_failed = w.get("failed", 0) w_earned = w.get("earned_rub", 0) total_success += w_success total_failed += w_failed w_ok24 = 0 w_earned24 = 0 for e in w.get("history_24h", []): if e.get("ts", 0) >= cutoff: if e.get("success"): w_ok24 += 1 w_earned24 += e.get("earned", 0) success_24h += 1 earned_24h += e.get("earned", 0) worker_lines.append( f" 👤 {w.get('username', wid)}\n" f" 24ч: ✅{w_ok24} 💵{w_earned24}$\n" f" Всего: ✅{w_success} ❌{w_failed} 💵{w_earned}$") workers_text = "\n".join(worker_lines) if worker_lines else " Нет воркеров" text = (f"🤖 ЗЕРКАЛО @{bname}\n" f"{'═' * 25}\n\n" f"👤 Владелец: @{owner_username} (ID: {owner_uid})\n" f"📅 Создано: {created}\n" f"🔔 Автовыплата: {ap} (порог {threshold}$)\n\n" f"💰 ДОХОД ВЛАДЕЛЬЦА\n" f" 📅 За 24ч: {earned_24h}$\n" f" 📆 Всё время: {earned_alltime}$\n" f" 💳 Выплачено: {paid}$\n" f" 💰 Баланс: {balance}$\n\n" f"📦 АКТИВАЦИИ\n" f" 📅 За 24ч: ✅ {success_24h}\n" f" 📆 Всё время: ✅ {total_success} ❌ {total_failed}\n\n" f"💵 ЦЕНЫ\n{prices_text}\n\n" f"👥 ВОРКЕРЫ ({len(workers)})\n{workers_text}") bot.send_message(call.message.chat.id, text)
@bot.message_handler(func=lambda m: m.text == "💸 Вывести") def request_payout(message): uid = message.from_user.id
# Защита от повторного вывода
if uid in payout_in_progress:
bot.send_message(message.chat.id, "⏳ Выплата уже в процессе. Подожди завершения.")
return
stats = load_stats()
s = stats.get(str(uid), {"success": 0, "failed": 0, "services": {}})
services_done = s.get("services", {})
if not services_done:
bot.send_message(message.chat.id, "💸 У тебя пока нет успешных активаций для выплаты.")
return
total_rub, breakdown = calc_payout(services_done)
if total_rub <= 0:
bot.send_message(message.chat.id, "💸 Сумма выплаты равна 0.")
return
# Цены уже в долларах — используем напрямую
amount_usdt = round(total_rub, 2)
text = (
f"💸 Расчёт выплаты:\n\n"
+ "\n".join(breakdown) +
f"\n\n💵 К выплате: {amount_usdt} USDT"
)
markup = telebot.types.InlineKeyboardMarkup()
markup.add(telebot.types.InlineKeyboardButton(
text=f"✅ Получить {amount_usdt} USDT",
callback_data=f"dopayout_{total_rub}_{amount_usdt}"
))
markup.add(telebot.types.InlineKeyboardButton(
text="❌ Отмена",
callback_data="cancelpayout"
))
bot.send_message(message.chat.id, text, reply_markup=markup)
@bot.callback_query_handler(func=lambda call: call.data.startswith("dopayout_")) def do_payout(call): uid = call.from_user.id
try:
bot.answer_callback_query(call.id)
except Exception:
pass
# Защита от повторного нажатия
if uid in payout_in_progress:
bot.edit_message_text(
"⏳ Выплата уже в процессе. Подожди.",
chat_id=call.message.chat.id,
message_id=call.message.message_id)
return
payout_in_progress.add(uid)
# Пересчитываем сумму из ТЕКУЩИХ сервисов (а не из кнопки!)
stats = load_stats()
s = stats.get(str(uid), {"success": 0, "failed": 0, "services": {}})
services_done = s.get("services", {})
if not services_done or s.get("success", 0) == 0:
payout_in_progress.discard(uid)
bot.edit_message_text(
"❌ Нет активаций для выплаты.",
chat_id=call.message.chat.id,
message_id=call.message.message_id)
return
total_rub, breakdown = calc_payout(services_done)
amount_usdt = round(total_rub, 2)
if amount_usdt <= 0:
payout_in_progress.discard(uid)
bot.edit_message_text(
"❌ Сумма выплаты равна 0.",
chat_id=call.message.chat.id,
message_id=call.message.message_id)
return
bot.edit_message_text(
"⏳ Отправляю выплату...",
chat_id=call.message.chat.id,
message_id=call.message.message_id
)
result, error = create_check(amount_usdt, uid)
if result:
check_url = result.get("bot_check_url", "")
# Сначала сбрасываем стату, потом отправляем
reset_stat(uid)
payout_in_progress.discard(uid)
bot.send_message(
call.message.chat.id,
f"✅ Чек создан!\n💵 {amount_usdt} USDT\n\n"
f"Нажми на ссылку чтобы получить деньги:\n{check_url}",
reply_markup=admin_menu() if uid in ADMIN_IDS else main_menu()
)
uname = s.get("username", str(uid))
add_payout_history(uid, uname, total_rub, amount_usdt, breakdown)
if uid not in ADMIN_IDS:
notify_admins_payout(uid, uname, total_rub, amount_usdt, "основной бот")
else:
payout_in_progress.discard(uid)
bot.send_message(
call.message.chat.id,
f"❌ Ошибка выплаты: {error}\n\nОбратись к администратору.",
reply_markup=admin_menu() if uid in ADMIN_IDS else main_menu()
)
@bot.callback_query_handler(func=lambda call: call.data == "cancelpayout") def cancel_payout(call): bot.edit_message_text( "❌ Выплата отменена.", chat_id=call.message.chat.id, message_id=call.message.message_id ) try:
bot.answer_callback_query(call.id)
except Exception:
pass
@bot.message_handler(func=lambda m: m.text == "👥 Воркеры") def all_workers(message): if message.from_user.id not in ADMIN_IDS: return stats = load_stats() if not stats: bot.send_message(message.chat.id, "👥 Воркеров пока нет.") return bot.send_message( message.chat.id, "👥 Список воркеров:\nНажми на воркера чтобы открыть его кабинет.", reply_markup=workers_list_keyboard() )
@bot.callback_query_handler(func=lambda call: call.data.startswith("worker_")) def worker_detail(call): if call.from_user.id not in ADMIN_IDS: try:
bot.answer_callback_query(call.id, "Нет доступа")
except Exception:
pass
return
uid = call.data[7:]
stats = load_stats()
s = stats.get(uid)
if not s:
try:
bot.answer_callback_query(call.id, "Воркер не найден")
except Exception:
pass
return
services_done = s.get("services", {})
total_rub, breakdown = calc_payout(services_done)
amount_usdt = round(total_rub, 2)
text = (
f"📊 Кабинет воркера:\n\n"
f"{format_stats_text(s, s['username'])}\n\n"
f"💵 К выплате: {amount_usdt} USDT"
)
bot.edit_message_text(
text,
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=worker_detail_keyboard(uid)
)
try:
bot.answer_callback_query(call.id)
except Exception:
pass
@bot.callback_query_handler(func=lambda call: call.data.startswith("doreset_")) def do_reset(call): if call.from_user.id not in ADMIN_IDS: try:
bot.answer_callback_query(call.id, "Нет доступа")
except Exception:
pass
return
uid = call.data[8:]
stats = load_stats()
s = stats.get(uid)
if not s:
try:
bot.answer_callback_query(call.id, "Воркер не найден")
except Exception:
pass
return
username = s["username"]
reset_stat(uid)
try:
bot.answer_callback_query(call.id, f"✅ Статистика {username} сброшена!")
except Exception:
pass
stats = load_stats()
s = stats.get(uid, {"username": username, "success": 0, "failed": 0, "services": {}})
text = f"📊 Кабинет воркера:\n\n{format_stats_text(s, username)}\n\n🔄 Статистика сброшена!"
bot.edit_message_text(
text,
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=worker_detail_keyboard(uid)
)
@bot.callback_query_handler(func=lambda call: call.data == "back_workers") def back_to_workers(call): if call.from_user.id not in ADMIN_IDS: try:
bot.answer_callback_query(call.id)
except Exception:
pass
return
stats = load_stats()
if not stats:
bot.edit_message_text("👥 Воркеров пока нет.", chat_id=call.message.chat.id, message_id=call.message.message_id)
return
bot.edit_message_text(
"👥 Список воркеров:\nНажми на воркера чтобы открыть его кабинет.",
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=workers_list_keyboard()
)
try:
bot.answer_callback_query(call.id)
except Exception:
pass
@bot.message_handler(func=lambda m: m.text == "📲 Новая активация") def new_activation(message): uid = message.from_user.id services = get_services() if not services: bot.send_message(message.chat.id, "❌ Не удалось получить список сервисов. Попробуй позже.") return user_state[uid] = {"step": "choose_services", "services": services, "selected_ids": []} bot.send_message( message.chat.id, "📋 Выбери сервисы (можно несколько), затем нажми Продолжить:", reply_markup=services_keyboard(services, []) )
@bot.callback_query_handler(func=lambda call: call.data.startswith("tog_")) def toggle_service(call): uid = call.from_user.id service_id = call.data[4:] state = user_state.get(uid, {}) if state.get("step") != "choose_services": try:
bot.answer_callback_query(call.id)
except Exception:
pass
return
selected = state.get("selected_ids", [])
if service_id in selected:
selected.remove(service_id)
else:
selected.append(service_id)
user_state[uid]["selected_ids"] = selected
bot.edit_message_reply_markup(
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=services_keyboard(state["services"], selected)
)
try:
bot.answer_callback_query(call.id)
except Exception:
pass
@bot.callback_query_handler(func=lambda call: call.data == "confirm_services") def confirm_services(call): uid = call.from_user.id state = user_state.get(uid, {}) selected = state.get("selected_ids", []) if not selected: try:
bot.answer_callback_query(call.id, "Выбери хотя бы один сервис!")
except Exception:
pass
return
services = state["services"]
names = [s["name"] for s in services if s["id"] in selected]
user_state[uid]["step"] = "enter_phone"
bot.edit_message_text(
f"✅ Выбрано сервисов: {len(selected)}\n" +
"\n".join(f"• {n}" for n in names) +
"\n\nВведи номер телефона в формате +79001234567:",
chat_id=call.message.chat.id,
message_id=call.message.message_id)
try:
bot.answer_callback_query(call.id)
except Exception:
pass
@bot.message_handler(func=lambda m: user_state.get(m.from_user.id, {}).get("step") == "enter_phone") def enter_phone(message): uid = message.from_user.id username = message.from_user.username or message.from_user.first_name or str(uid) phone = message.text.strip() clean = phone.replace("+", "").replace(" ", "").replace("-", "").replace("(", "").replace(")", "") if not clean.isdigit() or len(clean) < 10: bot.send_message(message.chat.id, "❌ Неверный формат. Введи номер, например: +79001234567") return
# Добавляем + если его нет
# Поддерживаем 7 и 8 (старый формат)
if clean.startswith("8"):
# 8XXXXXXXXXX → 79XXXXXXXXXX → +79XXXXXXXXXX
clean = "7" + clean[1:]
if not clean.startswith("7"):
bot.send_message(message.chat.id, "❌ Номер должен быть для России (начинаться на 7 или 8)")
return
formatted_phone = "+" + clean
state = user_state[uid]
selected_ids = state["selected_ids"]
services = state["services"]
msg = bot.send_message(
message.chat.id,
f"⏳ Создаю активации...\n📱 Номер: `{formatted_phone}`\n🔧 Сервисов: {len(selected_ids)}")
activations, error = create_activations(formatted_phone, selected_ids)
if not activations:
bot.edit_message_text(
f"❌ Ошибка: {error}",
chat_id=message.chat.id,
message_id=msg.message_id
)
user_state.pop(uid, None)
return
act_map = {}
for act in activations:
sid = act["service_id"]
svc = next((s for s in services if s["id"] == sid), None)
sname = svc["name"] if svc else sid
code_len = svc.get("sms_code_length", 6) if svc else 6
act_map[act["id"]] = {
"name": sname,
"status": "pending",
"code": None,
"sms_code_length": code_len,
"waiting_input": False
}
user_state[uid] = {
"step": "waiting_sms",
"phone": phone,
"username": username,
"act_map": act_map,
"pending_input": {}
}
status_msg = bot.edit_message_text(
build_status_text(phone, act_map),
chat_id=message.chat.id,
message_id=msg.message_id)
t = threading.Thread(
target=poll_all_activations,
args=(message.chat.id, uid, status_msg.message_id),
daemon=True
)
t.start()
def build_status_text(phone, act_map):
lines = [f"📱 Номер: {phone}\n"]
for act_id, info in act_map.items():
status = info["status"]
code = info["code"]
waiting = info.get("waiting_input", False)
if waiting:
icon, label = "⌨️", f"введи код ({info['sms_code_length']} цифр)"
elif status == "pending":
icon, label = "⏳", "ожидание"
elif status == "number_taken":
icon, label = "📡", "номер взят, ожидаю СМС"
elif status == "sms_sent":
icon, label = "📩", f"код принят: {code}" if code else "код принят"
elif status == "code_ready":
icon, label = "🔄", "регистрация..."
elif status == "completed":
icon, label = "✅", f"готово! код: {code}" if code else "готово!"
elif status == "failed":
icon, label = "❌", "ошибка"
elif status == "cancelled":
icon, label = "🚫", "отменено"
else:
icon, label = "🔄", status
lines.append(f"{icon} {info['name']} — {label}")
return "\n".join(lines)
def poll_all_activations(chat_id, uid, status_msg_id): max_wait = 300 elapsed = 0 username = user_state.get(uid, {}).get("username", str(uid))
while elapsed < max_wait:
state = user_state.get(uid, {})
if state.get("step") != "waiting_sms":
return
act_map = state["act_map"]
all_done = True
for act_id in list(act_map.keys()):
info = act_map[act_id]
if info["status"] in ("completed", "failed", "cancelled"):
continue
if info.get("waiting_input"):
all_done = False
continue
all_done = False
data = get_activation(act_id)
if not data:
continue
old_status = info["status"]
new_status = data["status"]
api_code = data.get("sms_code")
act_map[act_id]["status"] = new_status
if api_code:
act_map[act_id]["code"] = api_code
if old_status != "sms_sent" and new_status == "sms_sent":
act_map[act_id]["waiting_input"] = True
bot.send_message(
chat_id,
f"📩 {info['name']} — пришла СМС!\n\n"
f"Введи код ({info['sms_code_length']} цифр) для *{info['name']}*:")
state["pending_input"][act_id] = True
if old_status != new_status:
if new_status == "completed":
price = SERVICE_PRICES.get(info["name"], 0)
add_stat(uid, username, success=True, service_name=info["name"], earned_rub=price)
save_phone_record(uid, username, state.get("phone", "?"), "основной", info["name"], success=True)
elif new_status == "failed":
add_stat(uid, username, success=False)
save_phone_record(uid, username, state.get("phone", "?"), "основной", info["name"], success=False)
phone = state["phone"]
try:
bot.edit_message_text(
build_status_text(phone, act_map),
chat_id=chat_id,
message_id=status_msg_id)
except:
pass
if all_done:
menu = admin_menu() if uid in ADMIN_IDS else main_menu()
bot.send_message(chat_id, "✅ Все активации завершены!", reply_markup=menu)
user_state.pop(uid, None)
return
time.sleep(3)
elapsed += 3
act_map = user_state.get(uid, {}).get("act_map", {})
for act_id, info in act_map.items():
if info["status"] not in ("completed", "failed", "cancelled"):
cancel_activation(act_id)
add_stat(uid, username, success=False)
menu = admin_menu() if uid in ADMIN_IDS else main_menu()
bot.send_message(chat_id, "⏰ Время ожидания истекло. Активации отменены.", reply_markup=menu)
user_state.pop(uid, None)
@bot.message_handler(func=lambda m: ( user_state.get(m.from_user.id, {}).get("step") == "waiting_sms" and bool(user_state.get(m.from_user.id, {}).get("pending_input")) and m.text not in ["📲 Новая активация", "📊 Моя статистика", "👥 Воркеры", "💸 Вывести", "❌ Отмена", "🔗 Создать зеркало", "📢 Канал", "🏆 Топ воркеров", "📥 Экспорт CSV", "💰 Пополнить", "💼 Баланс", "📜 История выплат", "🔍 Все зеркала", "📈 Полная статистика", "📣 Рассылка", "💵 Цены"] )) def handle_sms_code_input(message): uid = message.from_user.id code = message.text.strip() state = user_state.get(uid, {}) act_map = state.get("act_map", {}) pending = state.get("pending_input", {})
if not code.isdigit():
bot.send_message(message.chat.id, "❌ Код должен состоять только из цифр. Попробуй ещё раз.")
return
target_id = None
for act_id in pending:
if act_map.get(act_id, {}).get("waiting_input"):
target_id = act_id
break
if not target_id:
return
info = act_map[target_id]
expected_len = info["sms_code_length"]
if len(code) != expected_len:
bot.send_message(message.chat.id, f"❌ Код должен быть {expected_len} цифр. Попробуй ещё раз.")
return
ok, error = send_sms_code(target_id, code)
if ok:
act_map[target_id]["waiting_input"] = False
act_map[target_id]["code"] = code
act_map[target_id]["status"] = "code_ready"
pending.pop(target_id, None)
bot.send_message(message.chat.id, f"✅ Код для {info['name']} принят! Ожидаю завершения регистрации...")
else:
bot.send_message(message.chat.id, f"❌ Ошибка отправки кода: {error}\nПопробуй ввести снова.")
@bot.message_handler(func=lambda m: m.text == "💼 Баланс" and m.from_user.id in ADMIN_IDS) def show_balance(message): balance = get_app_balance() if balance is None: bot.send_message(message.chat.id, "❌ Не удалось получить баланс.") return rate = get_usdt_rate() balance_rub = round(balance * rate, 2) bot.send_message( message.chat.id, f"💼 Баланс приложения:\n\n💵 {balance} USDT\n💰 ≈ {balance_rub}$")
@bot.message_handler(func=lambda m: m.text == "💰 Пополнить" and m.from_user.id in ADMIN_IDS) def topup_balance(message): markup = telebot.types.InlineKeyboardMarkup() amounts = [1, 2, 5, 10, 20] for a in amounts: markup.add(telebot.types.InlineKeyboardButton( text=f"💵 {a} USDT", callback_data=f"topup_{a}" )) bot.send_message(message.chat.id, "💰 Выбери сумму пополнения:", reply_markup=markup)
@bot.callback_query_handler(func=lambda call: call.data.startswith("topup_")) def do_topup(call): if call.from_user.id not in ADMIN_IDS: try:
bot.answer_callback_query(call.id, "Нет доступа")
except Exception:
pass
return
amount = call.data[6:]
try:
r = requests.post(
f"{CRYPTO_BOT_API}/createInvoice",
headers={"Crypto-Pay-API-Token": CRYPTO_BOT_TOKEN},
json={"asset": "USDT", "amount": amount},
timeout=10
)
data = r.json()
if data.get("ok"):
url = data["result"]["bot_invoice_url"]
markup = telebot.types.InlineKeyboardMarkup()
markup.add(telebot.types.InlineKeyboardButton(
text=f"💳 Оплатить {amount} USDT",
url=url
))
bot.edit_message_text(
f"💰 Инвойс на {amount} USDT создан!\n\nНажми кнопку ниже чтобы оплатить — деньги попадут на баланс приложения.",
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=markup
)
else:
error = data.get("error", {}).get("name", "Ошибка")
bot.edit_message_text(f"❌ Ошибка: {error}", chat_id=call.message.chat.id, message_id=call.message.message_id)
except Exception as e:
bot.edit_message_text(f"❌ Ошибка: {e}", chat_id=call.message.chat.id, message_id=call.message.message_id)
try:
bot.answer_callback_query(call.id)
except Exception:
pass
@bot.message_handler(func=lambda m: m.text == "📜 История выплат" and m.from_user.id in ADMIN_IDS) def payout_history(message): history = get_payout_history() if not history: bot.send_message(message.chat.id, "📜 История выплат пуста.") return # Показываем последние 20 записей lines = ["📜 История выплат:"] lines.append("") for h in reversed(history[-20:]): lines.append("👤 " + h["username"] + " | " + h["date"]) lines.append("💵 " + str(h["amount_usdt"]) + " USDT (≈" + str(h["amount_rub"]) + "$)") lines.append("") bot.send_message(message.chat.id, chr(10).join(lines))
@bot.message_handler(commands=['cancel']) @bot.message_handler(func=lambda m: m.text == "❌ Отмена") def cancel(message): uid = message.from_user.id state = user_state.get(uid, {}) act_map = state.get("act_map", {}) for act_id, info in act_map.items(): if info["status"] not in ("completed", "failed", "cancelled"): cancel_activation(act_id) menu = admin_menu() if uid in ADMIN_IDS else main_menu() bot.send_message(message.chat.id, "✅ Отменено.", reply_markup=menu) user_state.pop(uid, None)
@bot.message_handler(func=lambda m: m.text == "🏆 Топ воркеров" and m.from_user.id in ADMIN_IDS) def top_workers(message): stats = load_stats() if not stats: bot.send_message(message.chat.id, "👥 Воркеров пока нет.") return rate = get_usdt_rate() medals = ["🥇", "🥈", "🥉"] + [f"{i}." for i in range(4, 11)] by_act = sorted(stats.items(), key=lambda x: x[1].get("success", 0), reverse=True)[:10] lines = ["🏆 Топ по активациям:\n"] for i, (uid, s) in enumerate(by_act): if s.get("success", 0) <= 0: continue lines.append(f"{medals[i]} {s.get('username', uid)} — {s.get('success', 0)} ✅") by_earn = sorted(stats.items(), key=lambda x: x[1].get("earned_rub", 0), reverse=True)[:10] lines.append("\n💵 Топ по заработку:\n") for i, (uid, s) in enumerate(by_earn): earned = s.get("earned_rub", 0) if earned <= 0: continue lines.append(f"{medals[i]} {s.get('username', uid)} — {earned}$") bot.send_message(message.chat.id, "\n".join(lines))
@bot.message_handler(func=lambda m: m.text == "📥 Экспорт CSV" and m.from_user.id in ADMIN_IDS) def export_csv(message): stats = load_stats() if not stats: bot.send_message(message.chat.id, "📊 Нет данных для экспорта.") return rate = get_usdt_rate() markup = telebot.types.InlineKeyboardMarkup() markup.add(telebot.types.InlineKeyboardButton("📄 CSV", callback_data="export_csv")) if EXCEL_AVAILABLE: markup.add(telebot.types.InlineKeyboardButton("📊 Excel (XLSX)", callback_data="export_xlsx")) bot.send_message(message.chat.id, "Выбери формат экспорта:", reply_markup=markup)
@bot.callback_query_handler(func=lambda call: call.data == "export_csv") def export_csv_file(call): try: bot.answer_callback_query(call.id) except Exception: pass stats = load_stats() rate = get_usdt_rate() lines = ["Воркер,Успешных,Неудачных,Заработано_руб,Заработано_USDT,Выведено_руб"] for uid, s in stats.items(): earned = s.get("earned_rub", 0) paid = s.get("paid_rub", 0) lines.append(f"@{s.get('username', uid)},{s.get('success', 0)},{s.get('failed', 0)}," f"{earned},{round(earned/rate, 2)},{paid}") csv_bytes = "\n".join(lines).encode("utf-8-sig") filename = f"stats_{time.strftime('%d%m%Y_%H%M')}.csv" bot.send_document(call.message.chat.id, (filename, io.BytesIO(csv_bytes), "text/csv"))
@bot.callback_query_handler(func=lambda call: call.data == "export_xlsx") def export_xlsx_file(call): try: bot.answer_callback_query(call.id) except Exception: pass if not EXCEL_AVAILABLE: bot.send_message(call.message.chat.id, "❌ Excel недоступен на сервере.") return stats = load_stats() rate = get_usdt_rate() wb = openpyxl.Workbook() ws = wb.active ws.title = "Статистика" headers = ["Воркер", "Успешных", "Неудачных", "Заработано $", "Заработано USDT", "Выведено $"] ws.append(headers) header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid") for cell in ws[1]: cell.font = Font(bold=True, color="FFFFFF") cell.fill = header_fill cell.alignment = Alignment(horizontal="center") for uid, s in stats.items(): earned = s.get("earned_rub", 0) ws.append([f"@{s.get('username', uid)}", s.get("success", 0), s.get("failed", 0), earned, round(earned/rate, 2), s.get("paid_rub", 0)]) bio = io.BytesIO() wb.save(bio) bio.seek(0) filename = f"stats_{time.strftime('%d%m%Y_%H%M')}.xlsx" bot.send_document(call.message.chat.id, (filename, bio, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
@bot.message_handler(func=lambda m: m.text == "📢 Канал") def channel_button(message): markup = telebot.types.InlineKeyboardMarkup() markup.add(telebot.types.InlineKeyboardButton("📢 Перейти в канал", url=CHANNEL_URL)) bot.send_message(message.chat.id, "📢 Наш канал\n\nНовости, обновления и важные объявления:", reply_markup=markup)
@bot.message_handler(func=lambda m: m.text == "💵 Цены" and m.from_user.id in ADMIN_IDS) def admin_prices(message): lines = ["💵 Текущие цены основного бота:\n"] markup = telebot.types.InlineKeyboardMarkup() for sname, price in SERVICE_PRICES.items(): lines.append(f" • {sname} — {price}$") markup.add(telebot.types.InlineKeyboardButton( f"✏️ {sname} ({price}$)", callback_data=f"editprice_{sname}")) bot.send_message(message.chat.id, "\n".join(lines), reply_markup=markup)
@bot.callback_query_handler(func=lambda call: call.data.startswith("editprice_")) def edit_price_start(call): try: bot.answer_callback_query(call.id) except Exception: pass uid = call.from_user.id if uid not in ADMIN_IDS: return sname = call.data[10:] current = SERVICE_PRICES.get(sname, 0) user_state[uid] = {"step": "edit_base_price", "service": sname} bot.send_message(call.message.chat.id, f"✏️ Изменение цены: {sname}\n" f"Текущая цена: {current}$\n\n" f"Введи новую цену (в рублях):")
@bot.message_handler(func=lambda m: user_state.get(m.from_user.id, {}).get("step") == "edit_base_price") def edit_price_apply(message): uid = message.from_user.id state = user_state.get(uid, {}) sname = state.get("service", "") text = message.text.strip() if text == "❌ Отмена": user_state.pop(uid, None) bot.send_message(message.chat.id, "✅ Отменено.", reply_markup=admin_menu()) return try: new_price = float(text) if new_price < 0: bot.send_message(message.chat.id, "❌ Цена не может быть отрицательной. Попробуй снова:") return except ValueError: bot.send_message(message.chat.id, "❌ Введи число (например 0.15). Попробуй снова:") return user_state.pop(uid, None) old_price = SERVICE_PRICES.get(sname, 0) SERVICE_PRICES[sname] = new_price
# Спрашиваем уведомить ли воркеров
markup = telebot.types.InlineKeyboardMarkup()
markup.add(telebot.types.InlineKeyboardButton(
"📣 Да, уведомить", callback_data=f"notify_yes_{sname}_{old_price}_{new_price}"))
markup.add(telebot.types.InlineKeyboardButton(
"🔇 Нет, без уведомления", callback_data=f"notify_no"))
bot.send_message(message.chat.id,
f"✅ Цена обновлена!\n\n"
f" {sname}: {old_price}$ → {new_price}$\n\n"
f"Уведомить воркеров об изменении?",
reply_markup=markup)
@bot.callback_query_handler(func=lambda call: call.data.startswith("notify_yes_")) def notify_workers_yes(call): try: bot.answer_callback_query(call.id) except Exception: pass uid = call.from_user.id if uid not in ADMIN_IDS: return parts = call.data[11:].rsplit("_", 2) sname = parts[0] old_price = parts[1] new_price = parts[2]
stats = load_stats()
notification = (f"💲💲💲 ИЗМЕНЕНИЕ ЦЕНЫ 💲💲💲\n\n"
f"📌 {sname}\n"
f"💰 Было: {old_price}$\n"
f"💵 Стало: {new_price}$\n\n"
f"💲 Новая цена действует прямо сейчас!")
sent = 0
for worker_uid in stats.keys():
try:
bot.send_message(int(worker_uid), notification)
sent += 1
except Exception:
pass
bot.send_message(call.message.chat.id,
f"📣 Уведомление отправлено {sent} воркерам!",
reply_markup=admin_menu())
@bot.callback_query_handler(func=lambda call: call.data == "notify_no") def notify_workers_no(call): try: bot.answer_callback_query(call.id) except Exception: pass bot.send_message(call.message.chat.id, "🔇 Без уведомления.", reply_markup=admin_menu())
@bot.message_handler(func=lambda m: m.text == "📣 Рассылка" and m.from_user.id in ADMIN_IDS) def admin_broadcast(message): uid = message.from_user.id # Считаем воркеров основного бота (не в зеркалах) stats = load_stats() mirrors = load_mirrors() mirror_worker_ids = set() for token, m in mirrors.items(): for wid in m.get("workers", {}).keys(): mirror_worker_ids.add(str(wid)) main_workers = [uid for uid in stats.keys() if uid not in mirror_worker_ids] user_state[uid] = {"step": "broadcast_text"} bot.send_message(message.chat.id, f"📣 Рассылка для воркеров ОСНОВНОГО бота\n\n" f"👥 Получателей: {len(main_workers)}\n\n" f"Введи текст рассылки:\n" f"(или нажми ❌ Отмена)")
@bot.message_handler(func=lambda m: user_state.get(m.from_user.id, {}).get("step") == "broadcast_text") def admin_broadcast_send(message): uid = message.from_user.id text = message.text.strip() if text == "❌ Отмена": user_state.pop(uid, None) bot.send_message(message.chat.id, "✅ Отменено.", reply_markup=admin_menu()) return user_state.pop(uid, None) stats = load_stats() mirrors = load_mirrors() mirror_worker_ids = set() for token, m in mirrors.items(): for wid in m.get("workers", {}).keys(): mirror_worker_ids.add(str(wid)) sent = 0 failed = 0 for worker_uid in stats.keys(): if worker_uid in mirror_worker_ids: continue try: bot.send_message(int(worker_uid), f"📣 Объявление от админа:\n\n{text}") sent += 1 except Exception: failed += 1 bot.send_message(message.chat.id, f"📣 Рассылка завершена!\n\n" f"✅ Доставлено: {sent}\n" f"❌ Не доставлено: {failed}", reply_markup=admin_menu())
@bot.message_handler(func=lambda m: m.text == "🔗 Создать зеркало") def mirror_menu(message): uid = message.from_user.id my_mirrors = get_user_mirrors(uid) markup = telebot.types.InlineKeyboardMarkup() markup.add(telebot.types.InlineKeyboardButton("🤖 Создать зеркало", callback_data="mir_create")) if my_mirrors: markup.add(telebot.types.InlineKeyboardButton( f"📋 Мои зеркала ({len(my_mirrors)})", callback_data="mir_list")) bot.send_message(message.chat.id, "🪞 Зеркала\n\n" "Создай собственного бота-зеркало с твоими ценами. " "Воркеры регистрируются у тебя, а ты получаешь разницу в цене.\n\n" "Для создания нужен токен бота от @BotFather.", reply_markup=markup)
@bot.message_handler(func=lambda m: m.text == "🔍 Все зеркала" and m.from_user.id in ADMIN_IDS) def admin_all_mirrors(message): mirrors = load_mirrors() if not mirrors: bot.send_message(message.chat.id, "🔍 Зеркал не создано.") return chunks = [] current = "🔍 Все зеркала в системе:\n\n" for token, m in mirrors.items(): owner_uid = int(m.get("owner_uid", 0)) workers = m.get("workers", {}) total_act = sum(w.get("success", 0) for w in workers.values()) balance = round(m.get("owner_earned_rub", 0) - m.get("owner_paid_rub", 0), 2) block = (f"🤖 @{m.get('bot_username','?')}\n" f" 👤 @{m.get('owner_username', '?')} | 👥 {len(workers)}\n" f" ✅ {total_act} | 💰 {balance}$\n\n") if len(current) + len(block) > 3900: chunks.append(current) current = "" current += block if current: chunks.append(current) for chunk in chunks: bot.send_message(message.chat.id, chunk)
@bot.callback_query_handler(func=lambda call: call.data == "mir_create") def mirror_create_start(call): try: bot.answer_callback_query(call.id) except Exception: pass uid = call.from_user.id user_state[uid] = {"step": "mirror_token"} bot.send_message(call.message.chat.id, "🤖 Отправь токен нового бота (получи у @BotFather):")
@bot.message_handler(func=lambda m: user_state.get(m.from_user.id, {}).get("step") == "mirror_token") def mirror_enter_token(message): uid = message.from_user.id token = message.text.strip().replace("\n", "").replace("\r", "").replace(" ", "") try: test_bot = telebot.TeleBot(token) info = test_bot.get_me() bot_username = info.username except Exception: bot.send_message(message.chat.id, "❌ Неверный токен. Попробуй ещё раз или нажми ❌ Отмена.") return existing = load_mirrors() if token in existing: bot.send_message(message.chat.id, "❌ Это зеркало уже создано.") user_state.pop(uid, None) return services = list(SERVICE_PRICES.keys()) user_state[uid] = {"step": "mirror_prices", "token": token, "bot_username": bot_username, "prices": {}, "service_idx": 0, "services": services} first = services[0] bot.send_message(message.chat.id, f"✅ Бот @{bot_username} найден!\n\n" f"Теперь установи цены для воркеров (в долларах).\n" f"Разница между базовой ценой и твоей — твой доход.\n\n" f"💰 {first} (базовая {SERVICE_PRICES[first]}$)\n" f"Введи цену для воркеров (0 — отключить сервис):")
@bot.message_handler(func=lambda m: user_state.get(m.from_user.id, {}).get("step") == "mirror_prices") def mirror_set_price(message): uid = message.from_user.id try: price = float(message.text.strip()) except ValueError: bot.send_message(message.chat.id, "❌ Введи число.") return state = user_state[uid] services = state["services"] idx = state["service_idx"] sname = services[idx] base = SERVICE_PRICES[sname] if price < 0 or price > base: bot.send_message(message.chat.id, f"❌ Цена должна быть от 0 до {base}$.") return state["prices"][sname] = price idx += 1 state["service_idx"] = idx if idx < len(services): nxt = services[idx] bot.send_message(message.chat.id, f"💰 {nxt} (базовая {SERVICE_PRICES[nxt]}$)\n" f"Введи цену для воркеров (0 — отключить):") else: token = state["token"] bot_username = state["bot_username"] prices = state["prices"] username = message.from_user.username or message.from_user.first_name or str(uid) create_mirror(uid, username, token, bot_username, prices) price_lines = "\n".join([f"• {k}: {v}$" for k, v in prices.items()]) bot.send_message(message.chat.id, f"✅ Зеркало @{bot_username} создано!\n\n" f"Твои цены:\n{price_lines}\n\n" f"🔔 Автовыплата дохода включена (порог {MIRROR_AUTOPAYOUT_MIN_RUB}$).\n" f"Бот запускается...", reply_markup=admin_menu() if uid in ADMIN_IDS else main_menu()) user_state.pop(uid, None) threading.Thread(target=launch_mirror_bot, args=(token,), daemon=True).start()
@bot.callback_query_handler(func=lambda call: call.data == "mir_list") def mirror_list(call): try: bot.answer_callback_query(call.id) except Exception: pass uid = call.from_user.id my_mirrors = get_user_mirrors(uid) if not my_mirrors: bot.send_message(call.message.chat.id, "📋 У тебя нет зеркал.") return rate = get_usdt_rate() for token, m in my_mirrors.items(): workers = m.get("workers", {}) total_act = sum(w.get("success", 0) for w in workers.values()) balance = m.get("owner_earned_rub", 0) - m.get("owner_paid_rub", 0) prefix = token.split(":")[0] ap = "ВКЛ ✅" if m.get("autopayout_enabled", True) else "ВЫКЛ ❌" text = (f"🤖 @{m.get('bot_username', '?')}\n\n" f"👥 Воркеров: {len(workers)}\n" f"📦 Всего активаций: {total_act}\n" f"💰 Твой доход к выплате: {balance}$\n" f"💵 Всего заработано: {m.get('owner_earned_rub', 0)}$\n" f"🔔 Автовыплата: {ap} (порог {m.get('autopayout_min_rub', MIRROR_AUTOPAYOUT_MIN_RUB)}$)") markup = telebot.types.InlineKeyboardMarkup() markup.add(telebot.types.InlineKeyboardButton("👥 Воркеры зеркала", callback_data=f"mirw_{prefix}")) markup.add( telebot.types.InlineKeyboardButton( f"🔔 Автовыплата: {'ВКЛ' if m.get('autopayout_enabled', True) else 'ВЫКЛ'}", callback_data=f"mirtog_{prefix}"), telebot.types.InlineKeyboardButton( f"⚙️ Порог: {m.get('autopayout_min_rub', MIRROR_AUTOPAYOUT_MIN_RUB)}$", callback_data=f"mirthr_{prefix}") ) if balance > 0: markup.add(telebot.types.InlineKeyboardButton("💰 Вывести доход", callback_data=f"mirpay_{prefix}")) bot.send_message(call.message.chat.id, text, reply_markup=markup)
def _find_mirror_by_prefix(prefix): for token, m in load_mirrors().items(): if token.split(":")[0] == prefix: return token, m return None, None
@bot.callback_query_handler(func=lambda call: call.data.startswith("mirw_")) def mirror_workers(call): try: bot.answer_callback_query(call.id) except Exception: pass prefix = call.data[5:] token, m = _find_mirror_by_prefix(prefix) if not m: bot.send_message(call.message.chat.id, "❌ Зеркало не найдено.") return workers = m.get("workers", {}) if not workers: bot.send_message(call.message.chat.id, "👥 В зеркале пока нет воркеров.") return rate = get_usdt_rate() lines = [f"👥 Воркеры @{m.get('bot_username','?')}:\n"] for wid, w in workers.items(): cutoff = int(time.time()) - 86400 recent = [e for e in w.get("history_24h", []) if e.get("ts", 0) >= cutoff] ok24 = sum(1 for e in recent if e.get("success")) lines.append(f"👤 {w.get('username', wid)}\n" f" ✅ {w.get('success', 0)} | 24ч: {ok24} | ❌ {w.get('failed', 0)}\n" f" 💵 принёс дохода: {w.get('earned_rub', 0)}$") bot.send_message(call.message.chat.id, "\n".join(lines))
@bot.callback_query_handler(func=lambda call: call.data.startswith("mirtog_")) def mirror_toggle_autopayout(call): prefix = call.data[7:] token, m = _find_mirror_by_prefix(prefix) if not m: try: bot.answer_callback_query(call.id, "Не найдено") except Exception: pass return mirrors = load_mirrors() new_val = not mirrors[token].get("autopayout_enabled", True) mirrors[token]["autopayout_enabled"] = new_val save_mirrors(mirrors) try: bot.answer_callback_query(call.id, f"Автовыплата {'включена' if new_val else 'выключена'}") except Exception: pass
@bot.callback_query_handler(func=lambda call: call.data.startswith("mirthr_")) def mirror_set_threshold(call): try: bot.answer_callback_query(call.id) except Exception: pass prefix = call.data[7:] uid = call.from_user.id user_state[uid] = {"step": "mirror_threshold", "prefix": prefix} bot.send_message(call.message.chat.id, "⚙️ Введи новый порог автовыплаты в рублях (минимум 10):")
@bot.message_handler(func=lambda m: user_state.get(m.from_user.id, {}).get("step") == "mirror_threshold") def mirror_threshold_input(message): uid = message.from_user.id try: val = int(message.text.strip()) except ValueError: bot.send_message(message.chat.id, "❌ Введи число.") return if val < 10: bot.send_message(message.chat.id, "❌ Минимум 10$.") return prefix = user_state[uid]["prefix"] token, m = _find_mirror_by_prefix(prefix) if token: mirrors = load_mirrors() mirrors[token]["autopayout_min_rub"] = val save_mirrors(mirrors) bot.send_message(message.chat.id, f"✅ Порог автовыплаты: {val}$", reply_markup=admin_menu() if uid in ADMIN_IDS else main_menu()) user_state.pop(uid, None)
@bot.callback_query_handler(func=lambda call: call.data.startswith("mirpay_")) def mirror_payout(call): try: bot.answer_callback_query(call.id) except Exception: pass prefix = call.data[7:] token, m = _find_mirror_by_prefix(prefix) if not m: bot.send_message(call.message.chat.id, "❌ Зеркало не найдено.") return balance = m.get("owner_earned_rub", 0) - m.get("owner_paid_rub", 0) if balance <= 0: bot.send_message(call.message.chat.id, "💸 Нет дохода для вывода.") return uid = call.from_user.id amount_usdt = round(balance, 2) result, error = create_check(amount_usdt, uid) if result: check_url = result.get("bot_check_url", "") mirrors = load_mirrors() mirrors[token]["owner_paid_rub"] = mirrors[token].get("owner_paid_rub", 0) + balance save_mirrors(mirrors) bot.send_message(call.message.chat.id, f"✅ Чек на доход создан!\n💵 {amount_usdt} USDT\n\n{check_url}") notify_admins_payout(uid, m.get("owner_username", str(uid)), balance, amount_usdt, f"доход зеркала @{m.get('bot_username','?')}") else: bot.send_message(call.message.chat.id, f"❌ Ошибка: {error}")
def mmenu_mirror_owner(): """Меню для владельца зеркала""" mk = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True) mk.add("📲 Новая активация") mk.row("📊 Моя статистика", "👥 Воркеры") mk.row("💸 Вывести", "💵 Цены") mk.row("📣 Рассылка", "❌ Отмена") return mk
def mmenu_mirror_worker(): """Меню для обычного воркера""" mk = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True) mk.add("📲 Новая активация") mk.add("📊 Моя статистика") mk.add("💸 Вывести") mk.add("❌ Отмена") return mk
def launch_mirror_bot(token): try: mirror_data = get_mirror_by_token(token) if not mirror_data or not mirror_data.get("active", True): return prices = mirror_data.get("prices", {}) bot_username = mirror_data.get("bot_username", "?")
# Автообновление: добавляем новые сервисы с базовой ценой
updated = False
for sname, base_price in SERVICE_PRICES.items():
if sname not in prices:
prices[sname] = base_price
updated = True
if updated:
mirrors = load_mirrors()
if token in mirrors:
mirrors[token]["prices"] = prices
save_mirrors(mirrors)
mbot = telebot.TeleBot(token)
mirror_bots[token] = mbot
mstates = mirror_user_states.setdefault(token, {})
@mbot.message_handler(commands=['start'])
def m_start(message):
uid = message.from_user.id
username = message.from_user.username or message.from_user.first_name or str(uid)
stats = load_stats()
if str(uid) not in stats:
stats[str(uid)] = {"username": username, "success": 0, "failed": 0,
"services": {}, "earned_rub": 0, "paid_rub": 0, "history_24h": []}
save_stats(stats)
# регистрируем воркера в зеркале сразу
mirrors = load_mirrors()
if token in mirrors:
w = mirrors[token].setdefault("workers", {})
if str(uid) not in w:
w[str(uid)] = {"username": username, "success": 0, "failed": 0, "services": {},
"earned_rub": 0, "history_24h": [],
"joined_at": time.strftime("%d.%m.%Y %H:%M")}
save_mirrors(mirrors)
owner_uid = int(mirror_data.get("owner_uid", 0))
menu = mmenu_mirror_owner() if uid == owner_uid else mmenu_mirror_worker()
mbot.send_message(message.chat.id,
"👋 Привет! Я бот для регистрации через SMS.\n\nНажми кнопку ниже чтобы начать:",
reply_markup=menu)
@mbot.message_handler(func=lambda m: m.text == "📊 Моя статистика")
def m_stats(message):
uid = message.from_user.id
stats = load_stats()
s = stats.get(str(uid), {"success": 0, "failed": 0, "services": {}, "earned_rub": 0})
ok24, fail24, earned24 = get_stats_24h(uid)
rate = get_usdt_rate()
total_earned = s.get("earned_rub", 0)
total_success = s.get("success", 0)
total_failed = s.get("failed", 0)
services_text = ""
services = s.get("services", {})
if services:
sorted_services = sorted(services.items(), key=lambda x: x[1], reverse=True)
svc_lines = [f" • {sname} — {count}" for sname, count in sorted_services]
services_text = "\n" + "\n".join(svc_lines)
text = (f"📊 Твоя статистика\n\n"
f"📅 За 24 часа:\n"
f" ✅ Успешных: {ok24}\n"
f" ❌ Неудачных: {fail24}\n"
f" 💵 Заработано: {earned24}$\n\n"
f"📆 За всё время:\n"
f" ✅ Успешных: {total_success}{services_text}\n"
f" ❌ Неудачных: {total_failed}\n"
f" 📦 Всего: {total_success + total_failed}\n"
f" 💵 Заработано: {total_earned}$")
mbot.send_message(message.chat.id, text)
@mbot.message_handler(func=lambda m: m.text == "👥 Воркеры")
def m_workers(message):
mirrors = load_mirrors()
if token not in mirrors:
mbot.send_message(message.chat.id, "❌ Зеркало не найдено.")
return
workers = mirrors[token].get("workers", {})
if not workers:
mbot.send_message(message.chat.id, "👥 В зеркале пока нет воркеров.")
return
rate = get_usdt_rate()
lines = ["👥 Воркеры зеркала:\n"]
for wid, w in workers.items():
total = w.get("success", 0) + w.get("failed", 0)
earned = w.get("earned_rub", 0)
lines.append(f"👤 {w.get('username', wid)}\n"
f" ✅ {w.get('success', 0)} | ❌ {w.get('failed', 0)} | 📦 {total}\n"
f" 💵 принёс дохода: {earned}$")
mbot.send_message(message.chat.id, "\n".join(lines))
@mbot.message_handler(func=lambda m: m.text == "💸 Вывести")
def m_payout(message):
uid = message.from_user.id
# Защита от повторного вывода
if uid in payout_in_progress:
mbot.send_message(message.chat.id, "⏳ Выплата уже в процессе. Подожди.")
return
stats = load_stats()
s = stats.get(str(uid), {"services": {}, "success": 0})
services_done = s.get("services", {})
if not services_done or s.get("success", 0) == 0:
mbot.send_message(message.chat.id, "💸 Нет успешных активаций для выплаты.")
return
# считаем по ценам зеркала
total_rub = 0
breakdown = []
for sname, count in services_done.items():
p = prices.get(sname, 0)
if p > 0 and count > 0:
amt = round(p * count, 2)
total_rub += amt
breakdown.append(f"• {sname}: {count} × {p}$ = {amt}$")
if total_rub <= 0:
mbot.send_message(message.chat.id, "💸 Сумма выплаты равна 0.")
return
amount_usdt = round(total_rub, 2)
text = (f"💸 Расчёт выплаты:\n\n" + "\n".join(breakdown) +
f"\n\n💵 К выплате: {amount_usdt} USDT")
markup = telebot.types.InlineKeyboardMarkup()
markup.add(telebot.types.InlineKeyboardButton(
f"✅ Получить {amount_usdt} USDT", callback_data=f"dopayout_{total_rub}_{amount_usdt}"))
markup.add(telebot.types.InlineKeyboardButton("❌ Отмена", callback_data="cancelpayout"))
mbot.send_message(message.chat.id, text, reply_markup=markup)
@mbot.callback_query_handler(func=lambda call: call.data.startswith("dopayout_"))
def m_dopayout(call):
try: mbot.answer_callback_query(call.id)
except Exception: pass
uid = call.from_user.id
# Защита от повторного нажатия
if uid in payout_in_progress:
mbot.edit_message_text("⏳ Выплата уже в процессе.",
chat_id=call.message.chat.id, message_id=call.message.message_id)
return
payout_in_progress.add(uid)
# Пересчитываем из текущих сервисов
stats = load_stats()
s = stats.get(str(uid), {"services": {}, "success": 0})
services_done = s.get("services", {})
if not services_done or s.get("success", 0) == 0:
payout_in_progress.discard(uid)
mbot.edit_message_text("❌ Нет активаций для выплаты.",
chat_id=call.message.chat.id, message_id=call.message.message_id)
return
total_rub = 0
breakdown = []
for sname, count in services_done.items():
p = prices.get(sname, 0)
if p > 0 and count > 0:
amt = round(p * count, 2)
total_rub += amt
breakdown.append(f"• {sname}: {count} × {p}$ = {amt}$")
amount_usdt = round(total_rub, 2)
if amount_usdt <= 0:
payout_in_progress.discard(uid)
mbot.edit_message_text("❌ Сумма выплаты равна 0.",
chat_id=call.message.chat.id, message_id=call.message.message_id)
return
mbot.edit_message_text("⏳ Отправляю выплату...", chat_id=call.message.chat.id,
message_id=call.message.message_id)
result, error = create_check(amount_usdt, uid)
if result:
check_url = result.get("bot_check_url", "")
# Сначала сбрасываем стату
reset_stat(uid)
payout_in_progress.discard(uid)
owner_uid = int(mirror_data.get("owner_uid", 0))
menu = mmenu_mirror_owner() if uid == owner_uid else mmenu_mirror_worker()
mbot.send_message(call.message.chat.id,
f"✅ Чек создан!\n💵 {amount_usdt} USDT\n\n{check_url}", reply_markup=menu)
stats = load_stats()
uname = stats.get(str(uid), {}).get("username", str(uid))
add_payout_history(uid, uname, total_rub, amount_usdt, breakdown)
notify_admins_payout(uid, uname, total_rub, amount_usdt, f"зеркало @{bot_username}")
else:
payout_in_progress.discard(uid)
owner_uid = int(mirror_data.get("owner_uid", 0))
menu = mmenu_mirror_owner() if uid == owner_uid else mmenu_mirror_worker()
mbot.send_message(call.message.chat.id, f"❌ Ошибка выплаты: {error}", reply_markup=menu)
@mbot.callback_query_handler(func=lambda call: call.data == "cancelpayout")
def m_cancelpay(call):
try: mbot.answer_callback_query(call.id)
except Exception: pass
try:
mbot.edit_message_text("❌ Выплата отменена.", chat_id=call.message.chat.id,
message_id=call.message.message_id)
except Exception:
pass
@mbot.message_handler(func=lambda m: m.text == "📲 Новая активация")
def m_new_activation(message):
uid = message.from_user.id
services = get_services()
if not services:
mbot.send_message(message.chat.id, "❌ Не удалось получить сервисы.")
return
# фильтруем только включённые в зеркале (цена > 0)
available = [s for s in services if prices.get(s["name"], 0) > 0]
if not available:
mbot.send_message(message.chat.id, "❌ Нет доступных сервисов.")
return
mstates[uid] = {"step": "choose", "services": available, "selected_ids": []}
mbot.send_message(message.chat.id, "📋 Выбери сервисы, затем нажми Продолжить:",
reply_markup=services_keyboard(available, [], custom_prices=prices))
@mbot.callback_query_handler(func=lambda call: call.data.startswith("tog_"))
def m_toggle(call):
try: mbot.answer_callback_query(call.id)
except Exception: pass
uid = call.from_user.id
sid = call.data[4:]
st = mstates.get(uid, {})
if st.get("step") != "choose":
return
sel = st.get("selected_ids", [])
if sid in sel:
sel.remove(sid)
else:
sel.append(sid)
st["selected_ids"] = sel
try:
mbot.edit_message_reply_markup(call.message.chat.id, call.message.message_id,
reply_markup=services_keyboard(st["services"], sel, custom_prices=prices))
except Exception:
pass
@mbot.callback_query_handler(func=lambda call: call.data == "confirm_services")
def m_confirm(call):
try: mbot.answer_callback_query(call.id)
except Exception: pass
uid = call.from_user.id
st = mstates.get(uid, {})
if not st.get("selected_ids"):
return
st["step"] = "enter_phone"
mbot.edit_message_text("📱 Введи номер телефона (+79001234567):",
chat_id=call.message.chat.id, message_id=call.message.message_id)
@mbot.message_handler(func=lambda m: mstates.get(m.from_user.id, {}).get("step") == "enter_phone")
def m_phone(message):
uid = message.from_user.id
username = message.from_user.username or message.from_user.first_name or str(uid)
phone = message.text.strip()
clean = phone.replace("+", "").replace(" ", "").replace("-", "").replace("(", "").replace(")", "")
if not clean.isdigit() or len(clean) < 10:
mbot.send_message(message.chat.id, "❌ Неверный формат номера.")
return
# Добавляем + если его нет
# Поддерживаем 7 и 8 (старый формат)
if clean.startswith("8"):
# 8XXXXXXXXXX → 79XXXXXXXXXX → +79XXXXXXXXXX
clean = "7" + clean[1:]
if not clean.startswith("7"):
mbot.send_message(message.chat.id, "❌ Номер должен быть для России (начинаться на 7 или 8)")
return
formatted_phone = "+" + clean
st = mstates[uid]
selected_ids = st["selected_ids"]
services = st["services"]
msg = mbot.send_message(message.chat.id, f"⏳ Создаю активации...\n📱 {formatted_phone}")
activations, error = create_activations(formatted_phone, selected_ids)
if not activations:
mbot.edit_message_text(f"❌ Ошибка: {error}", chat_id=message.chat.id, message_id=msg.message_id)
mstates.pop(uid, None)
return
act_map = {}
for act in activations:
sid = act["service_id"]
svc = next((s for s in services if s["id"] == sid), None)
sname = svc["name"] if svc else sid
code_len = svc.get("sms_code_length", 6) if svc else 6
act_map[act["id"]] = {"name": sname, "status": "pending", "code": None,
"sms_code_length": code_len, "waiting_input": False}
mstates[uid] = {"step": "waiting_sms", "phone": phone, "username": username,
"act_map": act_map, "pending_input": {}}
mbot.edit_message_text(build_status_text(phone, act_map), chat_id=message.chat.id,
message_id=msg.message_id)
threading.Thread(target=poll_mirror_activations,
args=(mbot, message.chat.id, uid, msg.message_id, mstates, token, prices, mirror_data),
daemon=True).start()
@mbot.message_handler(func=lambda m: (
mstates.get(m.from_user.id, {}).get("step") == "waiting_sms" and
bool(mstates.get(m.from_user.id, {}).get("pending_input")) and
m.text not in ["📲 Новая активация", "📊 Моя статистика", "💸 Вывести", "❌ Отмена", "👥 Воркеры", "📣 Рассылка", "💵 Цены"]
))
def m_sms_input(message):
uid = message.from_user.id
code = message.text.strip()
st = mstates.get(uid, {})
act_map = st.get("act_map", {})
pending = st.get("pending_input", {})
if not code.isdigit():
mbot.send_message(message.chat.id, "❌ Код должен состоять из цифр.")
return
target_id = None
for act_id in pending:
if act_map.get(act_id, {}).get("waiting_input"):
target_id = act_id
break
if not target_id:
return
info = act_map[target_id]
if len(code) != info["sms_code_length"]:
mbot.send_message(message.chat.id, f"❌ Код должен быть {info['sms_code_length']} цифр.")
return
ok, error = send_sms_code(target_id, code)
if ok:
act_map[target_id]["waiting_input"] = False
act_map[target_id]["code"] = code
act_map[target_id]["status"] = "code_ready"
pending.pop(target_id, None)
mbot.send_message(message.chat.id, f"✅ Код для {info['name']} принят!")
else:
mbot.send_message(message.chat.id, f"❌ Ошибка: {error}")
@mbot.message_handler(func=lambda m: m.text == "❌ Отмена")
def m_cancel(message):
uid = message.from_user.id
st = mstates.get(uid, {})
for act_id, info in st.get("act_map", {}).items():
if info["status"] not in ("completed", "failed", "cancelled"):
cancel_activation(act_id)
mstates.pop(uid, None)
owner_uid = int(mirror_data.get("owner_uid", 0))
menu = mmenu_mirror_owner() if uid == owner_uid else mmenu_mirror_worker()
mbot.send_message(message.chat.id, "✅ Отменено.", reply_markup=menu)
@mbot.message_handler(func=lambda m: m.text == "📣 Рассылка")
def m_broadcast(message):
uid = message.from_user.id
owner_uid = int(mirror_data.get("owner_uid", 0))
if uid != owner_uid:
return
mstates[uid] = {"step": "mirror_broadcast"}
mbot.send_message(message.chat.id,
"📣 Введи текст рассылки для воркеров этого зеркала:\n\n"
"(Отправь текст или нажми ❌ Отмена)")
@mbot.message_handler(func=lambda m: mstates.get(m.from_user.id, {}).get("step") == "mirror_broadcast")
def m_broadcast_send(message):
uid = message.from_user.id
owner_uid = int(mirror_data.get("owner_uid", 0))
if uid != owner_uid:
return
text = message.text.strip()
if text == "❌ Отмена":
mstates.pop(uid, None)
mbot.send_message(message.chat.id, "✅ Отменено.", reply_markup=mmenu_mirror_owner())
return
mstates.pop(uid, None)
mirrors = load_mirrors()
m = mirrors.get(token, {})
workers = m.get("workers", {})
sent = 0
failed = 0
for worker_uid in workers.keys():
try:
mbot.send_message(int(worker_uid), f"📣 Объявление:\n\n{text}")
sent += 1
except Exception:
failed += 1
mbot.send_message(message.chat.id,
f"📣 Рассылка завершена!\n\n"
f"✅ Доставлено: {sent}\n"
f"❌ Не доставлено: {failed}",
reply_markup=mmenu_mirror_owner())
@mbot.message_handler(func=lambda m: m.text == "💵 Цены")
def m_prices(message):
uid = message.from_user.id
owner_uid = int(mirror_data.get("owner_uid", 0))
if uid != owner_uid:
return
mirrors = load_mirrors()
current_prices = mirrors.get(token, {}).get("prices", prices)
lines = ["💵 Цены твоего зеркала:\n"]
markup = telebot.types.InlineKeyboardMarkup()
for sname, price in current_prices.items():
base = SERVICE_PRICES.get(sname, 0)
status = "✅" if price > 0 else "❌ выкл"
lines.append(f" {status} {sname} — {price}$ (база: {base}$)")
markup.add(telebot.types.InlineKeyboardButton(
f"✏️ {sname} ({price}$)",
callback_data=f"meditpr_{sname}"))
bot_msg = "\n".join(lines)
mbot.send_message(message.chat.id, bot_msg, reply_markup=markup)
@mbot.callback_query_handler(func=lambda call: call.data.startswith("meditpr_"))
def m_edit_price_start(call):
try:
mbot.answer_callback_query(call.id)
except Exception:
pass
uid = call.from_user.id
owner_uid = int(mirror_data.get("owner_uid", 0))
if uid != owner_uid:
return
sname = call.data[8:]
mirrors = load_mirrors()
current_price = mirrors.get(token, {}).get("prices", {}).get(sname, 0)
base = SERVICE_PRICES.get(sname, 0)
mstates[uid] = {"step": "mirror_edit_price", "service": sname}
mbot.send_message(call.message.chat.id,
f"✏️ Изменение цены: {sname}\n"
f"Текущая цена: {current_price}$\n"
f"Базовая цена: {base}$\n\n"
f"Введи новую цену (0 — выключить сервис):")
@mbot.message_handler(func=lambda m: mstates.get(m.from_user.id, {}).get("step") == "mirror_edit_price")
def m_edit_price_apply(message):
uid = message.from_user.id
owner_uid = int(mirror_data.get("owner_uid", 0))
if uid != owner_uid:
return
st = mstates.get(uid, {})
sname = st.get("service", "")
text = message.text.strip()
if text == "❌ Отмена":
mstates.pop(uid, None)
mbot.send_message(message.chat.id, "✅ Отменено.", reply_markup=mmenu_mirror_owner())
return
try:
new_price = float(text)
if new_price < 0:
mbot.send_message(message.chat.id, "❌ Цена не может быть отрицательной.")
return
base = SERVICE_PRICES.get(sname, 0)
if new_price > base:
mbot.send_message(message.chat.id,
f"❌ Цена не может быть выше базовой ({base}$).\n"
f"Введи значение от 0 до {base}:")
return
except ValueError:
mbot.send_message(message.chat.id, "❌ Введи число (например 0.10).")
return
mstates.pop(uid, None)
mirrors = load_mirrors()
if token in mirrors:
old_price = mirrors[token].get("prices", {}).get(sname, 0)
mirrors[token]["prices"][sname] = new_price
prices[sname] = new_price
save_mirrors(mirrors)
# Спрашиваем уведомить ли воркеров
markup = telebot.types.InlineKeyboardMarkup()
markup.add(telebot.types.InlineKeyboardButton(
"📣 Да, уведомить", callback_data=f"mnotify_yes_{sname}_{old_price}_{new_price}"))
markup.add(telebot.types.InlineKeyboardButton(
"🔇 Нет", callback_data=f"mnotify_no"))
mbot.send_message(message.chat.id,
f"✅ Цена обновлена!\n\n"
f" {sname}: {old_price}$ → {new_price}$\n\n"
f"Уведомить воркеров?",
reply_markup=markup)
else:
mbot.send_message(message.chat.id, "❌ Ошибка сохранения.", reply_markup=mmenu_mirror_owner())
@mbot.callback_query_handler(func=lambda call: call.data.startswith("mnotify_yes_"))
def m_notify_yes(call):
try:
mbot.answer_callback_query(call.id)
except Exception:
pass
parts = call.data[12:].rsplit("_", 2)
sname = parts[0]
old_price = parts[1]
new_price = parts[2]
mirrors_data = load_mirrors()
workers = mirrors_data.get(token, {}).get("workers", {})
notification = (f"💲💲💲 ИЗМЕНЕНИЕ ЦЕНЫ 💲💲💲\n\n"
f"📌 {sname}\n"
f"💰 Было: {old_price}$\n"
f"💵 Стало: {new_price}$\n\n"
f"💲 Новая цена действует прямо сейчас!")
sent = 0
for worker_uid in workers.keys():
try:
mbot.send_message(int(worker_uid), notification)
sent += 1
except Exception:
pass
mbot.send_message(call.message.chat.id,
f"📣 Уведомление отправлено {sent} воркерам!",
reply_markup=mmenu_mirror_owner())
@mbot.callback_query_handler(func=lambda call: call.data == "mnotify_no")
def m_notify_no(call):
try:
mbot.answer_callback_query(call.id)
except Exception:
pass
mbot.send_message(call.message.chat.id, "🔇 Без уведомления.", reply_markup=mmenu_mirror_owner())
print(f"Зеркало @{bot_username} запущено")
mbot.infinity_polling(skip_pending=True, timeout=20)
except Exception as e:
print(f"Ошибка зеркала {token[:10]}: {e}")
def poll_mirror_activations(mbot, chat_id, uid, msg_id, mstates, mirror_token, prices, mirror_data): max_wait = 300 elapsed = 0 username = mstates.get(uid, {}).get("username", str(uid)) owner_uid = int(mirror_data.get("owner_uid", 0)) while elapsed < max_wait: st = mstates.get(uid, {}) if st.get("step") != "waiting_sms": return act_map = st["act_map"] all_done = True for act_id in list(act_map.keys()): info = act_map[act_id] if info["status"] in ("completed", "failed", "cancelled"): continue if info.get("waiting_input"): all_done = False continue all_done = False data = get_activation(act_id) if not data: continue old_status = info["status"] new_status = data["status"] api_code = data.get("sms_code") act_map[act_id]["status"] = new_status if api_code: act_map[act_id]["code"] = api_code if old_status != "sms_sent" and new_status == "sms_sent": act_map[act_id]["waiting_input"] = True st["pending_input"][act_id] = True try: mbot.send_message(chat_id, f"📩 {info['name']} — пришла СМС!\nВведи код " f"({info['sms_code_length']} цифр):") except Exception: pass if old_status != new_status: if new_status == "completed": price = prices.get(info["name"], SERVICE_PRICES.get(info["name"], 0)) add_stat(uid, username, success=True, service_name=info["name"], earned_rub=price, mirror_token=mirror_token) save_phone_record(uid, username, st.get("phone", "?"), f"@{mirror_data.get('bot_username','?')}", info["name"], success=True) elif new_status == "failed": add_stat(uid, username, success=False, mirror_token=mirror_token) save_phone_record(uid, username, st.get("phone", "?"), f"@{mirror_data.get('bot_username','?')}", info["name"], success=False) phone = st["phone"] try: mbot.edit_message_text(build_status_text(phone, act_map), chat_id=chat_id, message_id=msg_id) except Exception: pass if all_done: try: menu = mmenu_mirror_owner() if uid == owner_uid else mmenu_mirror_worker() mbot.send_message(chat_id, "✅ Все активации завершены!", reply_markup=menu) except Exception: pass mstates.pop(uid, None) return time.sleep(3) elapsed += 3 mstates.pop(uid, None) try: menu = mmenu_mirror_owner() if uid == owner_uid else mmenu_mirror_worker() mbot.send_message(chat_id, "⏰ Время истекло. Активации отменены.", reply_markup=menu) except Exception: pass
def launch_all_mirrors(): mirrors = load_mirrors() for token, m in mirrors.items(): if m.get("active", True): threading.Thread(target=launch_mirror_bot, args=(token,), daemon=True).start() time.sleep(1)
print("Бот запущен...") launch_all_mirrors()
while True: try: bot.infinity_polling(skip_pending=True, timeout=20) except Exception as e: print(f"Ошибка polling: {e}") time.sleep(5)