diff --git a/bot.py b/bot.py
index 1242075..a1b2646 100644
--- a/bot.py
+++ b/bot.py
@@ -1,7 +1,6 @@
-"""
-PropFirmTracker Bot V4 — Professional Edition
+# PropFirmTracker Bot V4 — Professional Edition
All commands, admin panel, AI toggle, polished UX.
-"""
+
import asyncio
import functools
from datetime import datetime
@@ -15,12 +14,14 @@
get_user_stats, get_setting, set_setting
)
from config import (
- TELEGRAM_BOT_TOKEN, ADMIN_USER_IDS, PROP_FIRMS,
+ ADMIN_USER_IDS, PROP_FIRMS,
PREMIUM_PRICE_MONTHLY, PREMIUM_PRICE_YEARLY,
REFERRALS_NEEDED, REFERRAL_REWARD_DAYS,
CRYPTO_WALLET_USDT_TRC20, STRIPE_API_KEY, PREMIUM_CHANNEL_ID
)
+# Load Telegram bot token from environment variable
+TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
# ══════════════════════════════════════
# HELPERS
@@ -60,7 +61,6 @@ async def wrapper(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
def bot_name(ctx):
return ctx.bot.username
-
# ══════════════════════════════════════
# PUBLIC COMMANDS
# ══════════════════════════════════════
@@ -120,7 +120,6 @@ async def cmd_start(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
)
log_info(f"User started: {u.id} (@{u.username})", tag="BOT")
-
async def cmd_help(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
uid = update.effective_user.id
adm = is_admin(uid)
@@ -154,7 +153,6 @@ async def cmd_help(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(msg, parse_mode='HTML')
-
async def cmd_firms(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
scores = get_latest_trustpilot_scores()
@@ -183,7 +181,6 @@ async def cmd_firms(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(msg, parse_mode='HTML')
-
async def cmd_promos(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
promos = get_active_promos()
@@ -218,7 +215,6 @@ async def cmd_promos(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(msg, parse_mode='HTML', disable_web_page_preview=True)
-
async def cmd_scores(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
scores = get_latest_trustpilot_scores()
@@ -251,14 +247,14 @@ async def cmd_scores(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(msg, parse_mode='HTML')
-
async def cmd_compare(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
if len(ctx.args) < 2:
firms_list = ", ".join([f"{s}" for s in list(PROP_FIRMS.keys())[:6]])
await update.message.reply_text(
"🔍 Compare Firms\n\n"
f"Usage: /compare firm1 firm2\n\n"
- f"Available: {firms_list}, ...\n\n"
+ f"Available: {firms_list}, ...
+\n"
"Tip: use the slug name (no spaces)",
parse_mode='HTML'
)
@@ -298,551 +294,4 @@ async def cmd_compare(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(msg, parse_mode='HTML', disable_web_page_preview=True)
-
-@premium_required
-async def cmd_history(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- firm_filter = ctx.args[0].lower() if ctx.args else None
- changes = get_recent_changes(10, firm_filter)
-
- if not changes:
- await update.message.reply_text(
- "📜 Change History\n\nNo changes recorded yet.",
- parse_mode='HTML'
- )
- return
-
- msg = "📜 Recent Changes\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
-
- for c in changes:
- fn = PROP_FIRMS.get(c['firm_slug'], {}).get('name', c['firm_slug'])
- ct = c.get('change_type', '')
- icon = {"pricing_change": "💰", "rules_change": "📋", "new_promo": "🎟", "scam_alert": "🚨"}.get(ct, "🔄")
- ts = c.get('detected_at', '')[:10]
- summary = c.get('summary', '')
-
- # Take first meaningful line
- first_line = ""
- for line in summary.split('\n'):
- line = line.strip()
- if line and not line.startswith(('💰', '📋', '🎟', '🔄')):
- first_line = line[:80]
- break
- if not first_line:
- first_line = summary.split('\n')[0][:80] if summary else "Update detected"
-
- msg += f" {icon} {fn} · {ts}\n"
- msg += f" {first_line}\n\n"
-
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
- msg += "Filter: /history ftmo"
-
- await update.message.reply_text(msg, parse_mode='HTML')
-
-
-@premium_required
-async def cmd_scams(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- from database import get_connection
- conn = get_connection()
- alerts = conn.execute(
- "SELECT * FROM scam_alerts ORDER BY detected_at DESC LIMIT 10"
- ).fetchall()
- conn.close()
-
- if not alerts:
- await update.message.reply_text(
- "🚨 Scam Alerts\n\nNo warnings detected. All clear for now.",
- parse_mode='HTML'
- )
- return
-
- msg = "🚨 Scam & Warning Alerts\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
-
- for a in alerts:
- sev_icon = {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(a['severity'], "⚪")
- fn = PROP_FIRMS.get(a['firm_slug'], {}).get('name', a['firm_slug'])
- ts = a['detected_at'][:10]
-
- msg += f" {sev_icon} {fn} · {a['severity'].upper()}\n"
- msg += f" {a['description'][:100]}\n"
- msg += f" {ts}\n\n"
-
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
- msg += "⚡ Always do your own due diligence."
-
- await update.message.reply_text(msg, parse_mode='HTML')
-
-
-async def cmd_referral(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- uid = update.effective_user.id
- get_or_create_user(uid, update.effective_user.username, update.effective_user.first_name)
-
- rc = get_referral_code(uid)
- total = get_total_referrals(uid)
- current = get_referral_count(uid)
-
- link = f"https://t.me/{bot_name(ctx)}?start=REF_{rc}"
- progress = min(current, REFERRALS_NEEDED)
- bar = "▓" * progress + "░" * (REFERRALS_NEEDED - progress)
-
- msg = "👥 Referral Program\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- msg += f" Invite {REFERRALS_NEEDED} friends → earn {REFERRAL_REWARD_DAYS} days FREE\n\n"
- msg += f" Progress: {bar} {progress}/{REFERRALS_NEEDED}\n"
- msg += f" Total referrals: {total}\n\n"
- msg += f" Your link:\n"
- msg += f" {link}\n\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
- msg += "Share your link — every friend counts!"
-
- kb = [[InlineKeyboardButton(
- "📤 Share Link",
- url=f"https://t.me/share/url?url={link}&text=Track+prop+firm+changes+in+real-time!"
- )]]
-
- await update.message.reply_text(msg, parse_mode='HTML', reply_markup=InlineKeyboardMarkup(kb))
-
-
-async def cmd_premium(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- uid = update.effective_user.id
-
- if is_premium(uid):
- from database import get_connection
- conn = get_connection()
- r = conn.execute("SELECT premium_expires_at FROM users WHERE user_id=?", (uid,)).fetchone()
- conn.close()
- exp = r['premium_expires_at'][:10] if r and r['premium_expires_at'] else 'N/A'
-
- await update.message.reply_text(
- "💎 Premium Active\n"
- "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- f" Expires: {exp}\n\n"
- " You're getting real-time alerts,\n"
- " AI analysis, and scam warnings.\n\n"
- "━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
- "/referral to extend for free",
- parse_mode='HTML'
- )
- return
-
- msg = "💎 Premium Plan\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- msg += " ⚡ Real-time alerts (no 24h delay)\n"
- msg += " 🧠 AI-powered change analysis\n"
- msg += " 🚨 Instant scam warnings\n"
- msg += " 📜 Full change history\n"
- msg += " 🎟 Promo alerts before anyone\n\n"
- msg += f" Monthly ${PREMIUM_PRICE_MONTHLY}\n"
- msg += f" Yearly ${PREMIUM_PRICE_YEARLY} (save 33%)\n\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━"
-
- kb = []
- if STRIPE_API_KEY:
- kb.append([
- InlineKeyboardButton(f"💳 ${PREMIUM_PRICE_MONTHLY}/mo", callback_data="pay_stripe_monthly"),
- InlineKeyboardButton(f"💳 ${PREMIUM_PRICE_YEARLY}/yr", callback_data="pay_stripe_yearly")
- ])
- kb.append([InlineKeyboardButton("₿ Pay with USDT", callback_data="pay_crypto")])
- kb.append([InlineKeyboardButton("🆓 Earn Free Days", callback_data="referral_info")])
-
- await update.message.reply_text(msg, parse_mode='HTML', reply_markup=InlineKeyboardMarkup(kb))
-
-
-async def cmd_status(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- uid = update.effective_user.id
- db = get_or_create_user(uid, update.effective_user.username, update.effective_user.first_name)
-
- prem = is_premium(uid)
- adm = is_admin(uid)
-
- if adm and prem:
- badge = "👑 Admin + Premium"
- elif adm:
- badge = "👑 Admin"
- elif prem:
- badge = "💎 Premium"
- else:
- badge = "🆓 Free"
-
- exp = db.get('premium_expires_at', 'N/A')
- exp = exp[:10] if exp and exp != 'N/A' else '—'
- refs = get_total_referrals(uid)
-
- msg = "👤 Your Account\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- msg += f" ID: {uid}\n"
- msg += f" Plan: {badge}\n"
- msg += f" Expires: {exp}\n"
- msg += f" Referrals: {refs}\n\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━"
-
- if not prem and not adm:
- msg += "\n💎 /premium · /referral for free days"
-
- await update.message.reply_text(msg, parse_mode='HTML')
-
-
-async def cmd_support(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- await update.message.reply_text(
- "🆘 Support\n\n"
- "Contact @PropFirmTrackerSupport\n"
- "for any questions or issues.",
- parse_mode='HTML'
- )
-
-
-# ══════════════════════════════════════
-# CALLBACK QUERIES
-# ══════════════════════════════════════
-
-async def handle_callback(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- q = update.callback_query
- await q.answer()
- d = q.data
-
- if d == "firms_list":
- scores = get_latest_trustpilot_scores()
- msg = "📊 Monitored Firms\n\n"
- for slug, cfg in PROP_FIRMS.items():
- sd = scores.get(slug, {})
- sc = sd.get('score', 0)
- dot = "🟢" if sc >= 4.5 else "🟡" if sc >= 3.5 else "🔴" if sc > 0 else "⚪"
- msg += f" {dot} {cfg['name']} · {sc:.1f}/5\n" if sc > 0 else f" {dot} {cfg['name']} · —\n"
- msg += f"\n/scores · /compare"
- await q.edit_message_text(msg, parse_mode='HTML')
-
- elif d == "promos":
- ps = get_active_promos()
- msg = "🎟 Active Promos\n\n"
- if ps:
- for p in ps[:5]:
- fn = PROP_FIRMS.get(p['firm_slug'], {}).get('name', '?')
- msg += f" {fn}: {p.get('promo_code', '?')} {p.get('discount', '')}\n"
- else:
- msg += " No active promos right now.\n"
- msg += "\n/promos for full details"
- await q.edit_message_text(msg, parse_mode='HTML')
-
- elif d == "scores":
- scores = get_latest_trustpilot_scores()
- if scores:
- ss = sorted(scores.items(), key=lambda x: x[1].get('score', 0), reverse=True)
- msg = "⭐ Scores\n\n"
- for i, (slug, sd) in enumerate(ss[:5]):
- fn = PROP_FIRMS.get(slug, {}).get('name', slug)
- sc = sd.get('score', 0)
- msg += f" {i+1}. {fn} — {sc:.1f}/5\n"
- msg += "\n/scores for full leaderboard"
- else:
- msg = "⭐ Collecting scores..."
- await q.edit_message_text(msg, parse_mode='HTML')
-
- elif d == "premium_info":
- await q.edit_message_text(
- f"💎 Premium\n\n"
- f" ⚡ Real-time alerts\n 🧠 AI analysis\n 🚨 Scam warnings\n\n"
- f" ${PREMIUM_PRICE_MONTHLY}/mo · ${PREMIUM_PRICE_YEARLY}/yr\n\n"
- f"/premium to subscribe · /referral for free",
- parse_mode='HTML'
- )
-
- elif d == "referral_info":
- rc = get_referral_code(q.from_user.id)
- link = f"https://t.me/{bot_name(ctx)}?start=REF_{rc}"
- await q.edit_message_text(
- f"👥 Referral\n\n"
- f"Invite {REFERRALS_NEEDED} friends → {REFERRAL_REWARD_DAYS} days FREE\n\n"
- f"{link}\n\n/referral for details",
- parse_mode='HTML'
- )
-
- elif d == "pay_crypto":
- await q.edit_message_text(
- f"₿ Pay with USDT\n\n"
- f"Send ${PREMIUM_PRICE_MONTHLY} USDT (TRC20) to:\n\n"
- f"{CRYPTO_WALLET_USDT_TRC20}\n\n"
- f"After sending, contact @PropFirmTrackerSupport\nwith your TX hash.",
- parse_mode='HTML'
- )
-
- elif d in ("pay_stripe_monthly", "pay_stripe_yearly", "pay_stars"):
- await q.edit_message_text(
- "💳 Contact @PropFirmTrackerSupport to set up payment.",
- parse_mode='HTML'
- )
-
-
-# ══════════════════════════════════════
-# ADMIN COMMANDS
-# ══════════════════════════════════════
-
-@admin_required
-async def cmd_admin(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- st = get_user_stats()
- from database import get_connection
- conn = get_connection()
- tc = conn.execute("SELECT COUNT(*) as c FROM changes").fetchone()['c']
- tp = conn.execute("SELECT COUNT(*) as c FROM promos WHERE is_active=1").fetchone()['c']
- ts = conn.execute("SELECT COUNT(*) as c FROM scam_alerts").fetchone()['c']
- conn.close()
-
- ai_status = get_setting("ai_enabled", "off")
- ai_icon = "🟢" if ai_status == "on" else "🔴"
-
- msg = "👑 Admin Dashboard\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- msg += f" Users\n"
- msg += f" 👥 {st['total']} total · 💎 {st['premium']} premium · 🆕 {st['today_new']} today\n"
- msg += f" 💰 ~${st['premium'] * PREMIUM_PRICE_MONTHLY:.0f} MRR\n\n"
- msg += f" Data\n"
- msg += f" 📈 {tc} changes · 🎟 {tp} promos · 🚨 {ts} scam alerts\n\n"
- msg += f" Settings\n"
- msg += f" {ai_icon} AI Analysis: {ai_status.upper()}\n\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- msg += "Commands\n"
- msg += " /stats — Quick stats\n"
- msg += " /scrape — Force scrape\n"
- msg += " /activate ID [days]\n"
- msg += " /addvip ID [days]\n"
- msg += " /broadcast message\n"
- msg += " /ai on|off"
-
- await update.message.reply_text(msg, parse_mode='HTML')
-
-
-@admin_required
-async def cmd_stats(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- st = get_user_stats()
- from database import get_connection
- conn = get_connection()
- tc = conn.execute("SELECT COUNT(*) as c FROM changes").fetchone()['c']
- tp = conn.execute("SELECT COUNT(*) as c FROM promos WHERE is_active=1").fetchone()['c']
- ts = conn.execute("SELECT COUNT(*) as c FROM scam_alerts").fetchone()['c']
- conn.close()
-
- msg = "📊 Quick Stats\n\n"
- msg += f" 👥 {st['total']} · 💎 {st['premium']} · 🆕 {st['today_new']}\n"
- msg += f" 📈 {tc} changes · 🎟 {tp} promos · 🚨 {ts} scams\n"
- msg += f" 💰 ~${st['premium'] * PREMIUM_PRICE_MONTHLY:.0f} MRR"
-
- await update.message.reply_text(msg, parse_mode='HTML')
-
-
-@admin_required
-async def cmd_activate(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- if not ctx.args:
- await update.message.reply_text(
- "Usage: /activate USER_ID [days]\nDefault: 30 days",
- parse_mode='HTML'
- )
- return
-
- try:
- tid = int(ctx.args[0])
- except:
- await update.message.reply_text("❌ Invalid user ID.")
- return
-
- days = int(ctx.args[1]) if len(ctx.args) > 1 else 30
- get_or_create_user(tid)
- activate_premium(tid, days, "admin")
-
- await update.message.reply_text(
- f"✅ {tid} → {days} days Premium activated",
- parse_mode='HTML'
- )
-
-
-@admin_required
-async def cmd_addvip(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- if not ctx.args:
- await update.message.reply_text(
- "Usage: /addvip USER_ID [days]\nActivates Premium + sends invite link",
- parse_mode='HTML'
- )
- return
-
- try:
- tid = int(ctx.args[0])
- except:
- await update.message.reply_text("❌ Invalid user ID.")
- return
-
- days = int(ctx.args[1]) if len(ctx.args) > 1 else 30
- get_or_create_user(tid)
- activate_premium(tid, days, "admin_vip")
-
- # Create invite link
- invite = None
- if PREMIUM_CHANNEL_ID:
- try:
- link_obj = await ctx.bot.create_chat_invite_link(
- chat_id=PREMIUM_CHANNEL_ID, member_limit=1, name=f"VIP-{tid}"
- )
- invite = link_obj.invite_link
- except Exception as e:
- log_error(f"Invite link: {e}", tag="ADMIN")
-
- # Notify user
- notified = False
- try:
- notif = f"🎉 Welcome to Premium!\n\n"
- notif += f"You have {days} days of Premium access.\n\n"
- notif += " ⚡ Real-time alerts\n 🧠 AI analysis\n 🚨 Scam warnings\n\n"
- if invite:
- notif += f"Join the VIP channel:\n{invite}\n\n"
- notif += "Enjoy! 🚀"
- await ctx.bot.send_message(chat_id=tid, text=notif, parse_mode='HTML')
- notified = True
- except:
- pass
-
- await update.message.reply_text(
- f"✅ VIP {tid} · {days} days\n"
- f" 🔗 Invite: {'✅' if invite else '❌'}\n"
- f" 📨 Notified: {'✅' if notified else '❌'}",
- parse_mode='HTML'
- )
-
-
-@admin_required
-async def cmd_broadcast(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- if not ctx.args:
- await update.message.reply_text(
- "Usage: /broadcast your message here",
- parse_mode='HTML'
- )
- return
-
- msg = " ".join(ctx.args)
- from database import get_connection
- conn = get_connection()
- users = conn.execute("SELECT user_id FROM users WHERE is_banned=0").fetchall()
- conn.close()
-
- status = await update.message.reply_text(f"📢 Broadcasting to {len(users)} users...")
-
- sent, failed = 0, 0
- for u in users:
- try:
- await ctx.bot.send_message(chat_id=u['user_id'], text=msg, parse_mode='HTML')
- sent += 1
- await asyncio.sleep(0.05)
- except:
- failed += 1
-
- await status.edit_text(f"📢 Done · ✅ {sent} · ❌ {failed}")
-
-
-@admin_required
-async def cmd_scrape(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- await update.message.reply_text("🔍 Scraping all sources...")
-
- from scrapers import PropFirmScraper, RedditScraper, TrustpilotScraper
- results = []
-
- for name, Cls in [("Firms", PropFirmScraper), ("Reddit", RedditScraper), ("Trustpilot", TrustpilotScraper)]:
- try:
- r = Cls().scrape_all()
- errs = r.get('errors', 0)
- icon = "✅" if errs == 0 else "⚠️" if errs < 5 else "❌"
- results.append(f" {icon} {name}: {r}")
- except Exception as e:
- results.append(f" ❌ {name}: {e}")
-
- msg = "📊 Scrape Results\n"
- msg += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- msg += "\n".join(results)
-
- await update.message.reply_text(msg, parse_mode='HTML')
-
-
-@admin_required
-async def cmd_ai_toggle(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
- if not ctx.args:
- current = get_setting("ai_enabled", "off")
- icon = "🟢 ON" if current == "on" else "🔴 OFF"
- await update.message.reply_text(
- f"🧠 AI Analysis\n"
- f"━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
- f" Status: {icon}\n\n"
- f" When ON, Claude analyzes each detected\n"
- f" change and explains the impact for traders.\n\n"
- f" Cost: ~$0.01 per change\n\n"
- f"━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
- f" /ai on · /ai off",
- parse_mode='HTML'
- )
- return
-
- action = ctx.args[0].lower()
- if action == "on":
- set_setting("ai_enabled", "on")
- await update.message.reply_text(
- "🧠 AI Analysis: 🟢 ON\n\n"
- "Next scrape will include AI summaries.\n"
- "Cost: ~$0.01 per change detected.\n\n"
- "/ai off to disable anytime",
- parse_mode='HTML'
- )
- log_info("AI ENABLED by admin", tag="AI")
- elif action == "off":
- set_setting("ai_enabled", "off")
- await update.message.reply_text(
- "🧠 AI Analysis: 🔴 OFF\n\n"
- "Using text diff only (free).\n\n"
- "/ai on to enable",
- parse_mode='HTML'
- )
- log_info("AI DISABLED by admin", tag="AI")
- else:
- await update.message.reply_text("Usage: /ai on or off", parse_mode='HTML')
-
-
-# ══════════════════════════════════════
-# BOT SETUP
-# ══════════════════════════════════════
-
-async def error_handler(update, ctx):
- log_error(f"Bot error: {ctx.error}", tag="ERROR")
-
-
-async def post_init(app):
- commands = [
- BotCommand("start", "Start the bot"),
- BotCommand("help", "All commands"),
- BotCommand("firms", "Monitored firms"),
- BotCommand("promos", "Active promo codes"),
- BotCommand("scores", "Trustpilot leaderboard"),
- BotCommand("compare", "Compare two firms"),
- BotCommand("referral", "Earn free Premium"),
- BotCommand("premium", "Upgrade to Premium"),
- BotCommand("status", "Your account"),
- BotCommand("support", "Get help"),
- ]
- await app.bot.set_my_commands(commands)
- log_info(f"Bot ready · Admin IDs: {ADMIN_USER_IDS}", tag="STARTUP")
-
-
-def create_bot():
- log_info("Creating bot...", tag="STARTUP")
- app = Application.builder().token(TELEGRAM_BOT_TOKEN).post_init(post_init).build()
-
- handlers = [
- ("start", cmd_start), ("help", cmd_help), ("firms", cmd_firms),
- ("promos", cmd_promos), ("scores", cmd_scores), ("compare", cmd_compare),
- ("referral", cmd_referral), ("premium", cmd_premium), ("status", cmd_status),
- ("support", cmd_support), ("history", cmd_history), ("scams", cmd_scams),
- ("admin", cmd_admin), ("stats", cmd_stats), ("activate", cmd_activate),
- ("addvip", cmd_addvip), ("broadcast", cmd_broadcast), ("scrape", cmd_scrape),
- ("ai", cmd_ai_toggle),
- ]
- for cmd, func in handlers:
- app.add_handler(CommandHandler(cmd, func))
-
- app.add_handler(CallbackQueryHandler(handle_callback))
- app.add_error_handler(error_handler)
-
- log_info("All handlers registered", tag="STARTUP")
- return app
+... (truncated, 549 more lines)
\ No newline at end of file