diff options
Diffstat (limited to 'sl_bot.py')
| -rw-r--r-- | sl_bot.py | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/sl_bot.py b/sl_bot.py new file mode 100644 index 0000000..1fe5294 --- /dev/null +++ b/sl_bot.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python3 +import os, httpx, json, hashlib, logging +from datetime import datetime +from telegram import ( + Update, + InlineQueryResultArticle, + InputTextMessageContent, + InlineKeyboardButton, + InlineKeyboardMarkup, + LinkPreviewOptions, +) +from telegram.ext import ( + Application, + InlineQueryHandler, + ChosenInlineResultHandler, + MessageHandler, + CallbackQueryHandler, + filters, +) +import io + +logging.basicConfig( + format="%(asctime)s | %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + level=logging.INFO, +) +log = logging.getLogger(__name__) + +API_KEY = "dc56cf41ff66114030f7766e547f1d64d99356982093bb4b74506e88281ce198" +ALLOWED = {8066036773, 8546929916, 8303244085, 7661900680, 7316173289, 7838198850} +PROXY = "http://MW1BWBJK28_US_5_LOHMZVIP:[email protected]:8080" + + +import re + + +def process_response(text, fmt): + if fmt == "json": + try: + data = json.loads(text) + results = data.get("results", []) + return json.dumps(results, indent=2) + except: + return text + return text + + +def safe_filename(query): + name = re.sub(r'[<>:"/\\|?*]', "_", query) + name = re.sub(r"https?://", "", name) + name = name.strip("._")[:50] + return name or "results" + + +async def inline(update: Update, _): + user = update.inline_query.from_user + log.info( + f"INLINE | user={user.id} ({user.username}) | query='{update.inline_query.query}'" + ) + if user.id not in ALLOWED: + return + if not (query := update.inline_query.query.strip()): + return + formats = [ + ("ulp", "ULP", "URL:Login:Password format"), + ("lp", "LP", "Login:Password format"), + ("u", "URL", "URL only"), + ("l", "Login", "Login only"), + ("p", "Password", "Password only"), + ("json", "JSON", "Full JSON format"), + ] + await update.inline_query.answer( + [ + InlineQueryResultArticle( + id=fmt_id, + title=f"Search: {query} ({fmt_name})", + description=fmt_desc, + input_message_content=InputTextMessageContent(f"Searching: {query}..."), + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Loading...", callback_data="noop")]] + ), + ) + for fmt_id, fmt_name, fmt_desc in formats + ], + cache_time=0, + ) + + +async def chosen(update: Update, context): + user = update.chosen_inline_result.from_user + result_id = update.chosen_inline_result.result_id + log.info( + f"CHOSEN | user={user.id} ({user.username}) | query='{update.chosen_inline_result.query}' | format={result_id}" + ) + if user.id not in ALLOWED: + return + query = update.chosen_inline_result.query.strip() + if not query: + return + try: + inline_message_id = update.chosen_inline_result.inline_message_id + result_id = update.chosen_inline_result.result_id + fmt = result_id if result_id in ("ulp", "lp", "u", "l", "p", "json") else "ulp" + loading_markup = InlineKeyboardMarkup( + [[InlineKeyboardButton("⏳ Searching...", callback_data="noop")]] + ) + if inline_message_id: + await context.bot.edit_message_text( + inline_message_id=inline_message_id, + text=f"Searching: {query}...", + reply_markup=loading_markup, + ) + log.info(f"API | fetching format={fmt}") + async with httpx.AsyncClient(proxy=PROXY, timeout=120) as client: + resp = await client.get( + "https://db.org.ai/sl/search", + params={"query": query, "max_hits": 10000, "format": fmt}, + headers={"X-API-Key": API_KEY}, + ) + txt = ( + process_response(resp.text, fmt) + if resp.status_code == 200 + else "No results" + ) + lines = len(txt.splitlines()) + log.info(f"API | status={resp.status_code} | lines={lines}") + + if lines == 0: + if inline_message_id: + await context.bot.edit_message_text( + inline_message_id=inline_message_id, + text=f"❌ No results found for: {query}", + ) + return + + uploading_markup = InlineKeyboardMarkup( + [[InlineKeyboardButton("⏳ Uploading...", callback_data="noop")]] + ) + if inline_message_id: + await context.bot.edit_message_text( + inline_message_id=inline_message_id, + text=f"Uploading {lines} lines...", + reply_markup=uploading_markup, + ) + upload_headers = {"User-Agent": "curl/8.0.1"} + try: + upload = await client.post( + "https://0x.at", + files={"file": (f"{safe_filename(query)}.txt", txt.encode())}, + headers=upload_headers, + ) + url = upload.text.strip() + except Exception: + async with httpx.AsyncClient(timeout=30, verify=False) as upload_client: + upload = await upload_client.post( + "https://0x.at", + files={"file": (f"{safe_filename(query)}.txt", txt.encode())}, + headers=upload_headers, + ) + url = upload.text.strip() + log.info(f"UPLOAD | url={url}") + + if inline_message_id: + await context.bot.edit_message_text( + inline_message_id=inline_message_id, + text=f"🔗 {query} - {lines} lines ({fmt.upper()})", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("📥 Download", url=url)]] + ), + ) + log.info( + f"DONE | user={user.id} | query='{query}' | lines={lines} | url={url}" + ) + except httpx.TimeoutException: + log.error(f"TIMEOUT | user={user.id} | query='{query}'") + if inline_message_id: + await context.bot.edit_message_text( + inline_message_id=inline_message_id, + text=f"⏱️ Request timed out for: {query}", + ) + except Exception as e: + log.error(f"ERROR | user={user.id} | query='{query}' | error={e}") + + +async def dm(update: Update, context): + user = update.message.from_user + if user.id not in ALLOWED: + return + query = update.message.text.strip() + if not query: + return + log.info(f"DM | user={user.id} ({user.username}) | query='{query}'") + + format_buttons = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("ULP", callback_data=f"dm:ulp:{query}"), + InlineKeyboardButton("LP", callback_data=f"dm:lp:{query}"), + InlineKeyboardButton("JSON", callback_data=f"dm:json:{query}"), + ], + [ + InlineKeyboardButton("URL", callback_data=f"dm:u:{query}"), + InlineKeyboardButton("Login", callback_data=f"dm:l:{query}"), + InlineKeyboardButton("Password", callback_data=f"dm:p:{query}"), + ], + ] + ) + await update.message.reply_text( + f"Select format for: {query}", + reply_markup=format_buttons, + ) + + +async def dm_callback(update: Update, context): + query_obj = update.callback_query + await query_obj.answer() + + user = query_obj.from_user + if user.id not in ALLOWED: + return + + data = query_obj.data + if not data.startswith("dm:"): + return + + parts = data.split(":", 2) + if len(parts) != 3: + return + + _, fmt, query = parts + log.info( + f"DM_CALLBACK | user={user.id} ({user.username}) | query='{query}' | format={fmt}" + ) + + await query_obj.edit_message_text(f"⏳ Searching: {query}...") + + try: + async with httpx.AsyncClient(proxy=PROXY, timeout=120) as client: + resp = await client.get( + "https://db.org.ai/sl/search", + params={"query": query, "max_hits": 10000, "format": fmt}, + headers={"X-API-Key": API_KEY}, + ) + txt = process_response(resp.text, fmt) if resp.status_code == 200 else "" + lines = len(txt.splitlines()) + log.info(f"API | status={resp.status_code} | lines={lines}") + + if lines == 0: + await query_obj.edit_message_text(f"❌ No results found for: {query}") + log.info(f"DONE | user={user.id} | query='{query}' | no results") + return + + await query_obj.edit_message_text(f"⏳ Uploading {lines} lines...") + + await query_obj.message.reply_document( + document=io.BytesIO(txt.encode()), + filename=f"{safe_filename(query)}.txt", + caption=f"🔗 {query} - {lines} lines ({fmt.upper()})", + ) + await query_obj.delete_message() + log.info( + f"DONE | user={user.id} | query='{query}' | lines={lines} | sent document" + ) + except httpx.TimeoutException: + log.error(f"TIMEOUT | user={user.id} | query='{query}'") + await query_obj.edit_message_text(f"⏱️ Request timed out for: {query}") + except Exception as e: + log.error(f"ERROR | user={user.id} | query='{query}' | error={e}") + await query_obj.edit_message_text(f"❌ Error: {e}") + + +app = ( + Application.builder() + .token("8511396304:AAHn0da5vx7bLoMED7THD8on0mXNvNjY3Ks") + .build() +) +app.add_handler(InlineQueryHandler(inline)) +app.add_handler(ChosenInlineResultHandler(chosen)) +app.add_handler(MessageHandler(filters.TEXT & filters.ChatType.PRIVATE, dm)) +app.add_handler(CallbackQueryHandler(dm_callback, pattern="^dm:")) +app.run_polling() |
