summaryrefslogtreecommitdiffstats
path: root/sl_bot.py
diff options
context:
space:
mode:
Diffstat (limited to 'sl_bot.py')
-rw-r--r--sl_bot.py281
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()