Engineersatya/EmpireBot
0
1import time, random, gspread, asyncio, os, sys, re2import datetime as dt3from datetime import datetime, timedelta4import pytz5from oauth2client.service_account import ServiceAccountCredentials6from telegram import Update, Poll, InlineKeyboardButton, InlineKeyboardMarkup, WebAppInfo7from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes, MessageHandler, filters, PollAnswerHandler8import logging9 10# ๐ เคฏเฅ 2 เคฒเคพเคเคจเฅเค เคเฅเฅเคจเคพ เคฌเคนเฅเคค เฅเคฐเฅเคฐเฅ เคนเฅ ๐11from flask import Flask12from threading import Thread13 14logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')15 16# --- CONFIGURATION ---17BOT_TOKEN = '5180085614:AAGSm3d5x16neC4QTs2k3In6CLt1Nmck6mM'18MY_ADMIN_ID = 882736738 # ๐ YAHAN APNI NUMERIC ID DAALIYE19BRANDING = "\n\n๐Eอnอgอiอnอeอeอrอsอaอtอyอaอ๐"20DEFAULT_LIMIT = 5021NEG_MARK = 0.2522 23# ๐ GROUP CONFIGURATION (FREE VS PAID)24PUBLIC_GROUP = '@engineersatya'25PAID_GROUP = -100229556149026PAID_UP_GROUP = -100388905402527PAID_3RD_GROUP = -100246523119728 29GROUP_CONFIG = {30 PUBLIC_GROUP: '15Q0ST9CcvDbWZdBobGDDSAJAq80K4XiQhFxn4R8e_Dk',31 PAID_GROUP: '1Jc79jX4X5AV8-JMlUqRaDAe6g2ix1DReOJn8gj3h0Mk',32 PAID_UP_GROUP: "1NnoJS9Rx4Usn68vDer83fXUo6qLipchFlMEwg3AsDbg"33}34 35# ====================================================================36# ๐ง NAYA JADOO: AI SUBJECT AUTO-TAGGER (Keywords Dictionary)37# ====================================================================38SUBJECT_KEYWORDS = {39 "1. Building Materials & Construction (BMC)": ["Cement", "Concrete", "Brick", "Timber", "Aggregate", "Mortar", "Curing", "Workability", "Stone", "Granite", "Modulus of Elasticity", "RPC", "Phase-change material", "Paraffin wax", "FRP", "Vicat", "Setting time", "Frog", "Brinell", "Modular brick", "Marble", "Mohs", "Seasoning", "English bond", "Xylem", "Phloem", "Cambium", "Soundness", "ALAG", "Pozzolana", "Sulfate attack", "Water absorption", "Ballast", "Paint vehicle", "Annual rings", "Sound absorbent", "Cork", "Efflorescence", "Honeycombing", "Retarder", "Pointing", "Flexible thermal insulation", "Mineral wool", "Perpend", "Cement plaster", "Flaky", "Elongated aggregates", "Single scaffolding", "Monolithic floor", "Ashlar masonry", "ASCU treatment", "Jhama bricks"],40 "2. Soil Mechanics & Foundation Engineering": ["Void ratio", "Porosity", "Permeability", "Shear strength parameters", "Consolidation", "Primary creep", "Secondary creep", "Compaction", "Tensiometer", "Lime stabilization", "Terzaghi", "Uniformity coefficient", "IS Soil classification", "MDD", "OMC", "Bulking", "Se=wG", "Plastic limit", "Critical hydraulic gradient", "Angle of internal friction", "Bearing capacity", "Settlement in sand", "Aeolian soil", "Quicksand", "Piping", "Pycnometer", "Ultimate bearing capacity", "FOS", "Pore water pressure", "Effective stress", "Boreholes", "Slope stability", "Sudden drawdown", "Active earth pressure", "Pseudo-static method", "Vane shear test", "Sounding", "Raft footing", "Batter piles", "Negative skin friction", "Layered soil", "Dupuit's theory", "UU test", "B parameter", "Laplace", "Anisotropic", "Flow net", "Shape factor"],41 "3. Fluid Mechanics (FM) & Hydraulics": ["Viscosity", "Bernoulli", "Reynolds", "Mach", "Weir", "Flume", "Specific energy", "Open channel", "Continuity", "Conservation of mass", "Venturimeter", "Flow rate", "Piezometer", "Gauge pressure", "GVF", "Froude", "Water hammer", "EGL", "Hydraulic jump", "Pelton wheel", "Jet velocity", "Cavitation", "NPSH", "Hydrostatic pressure", "Fully developed flow", "Laminar", "Wetted perimeter", "Hydraulic radius", "Total acceleration", "Boundary layer", "separation point", "Buoyant force", "Turbulent flow", "Eddies", "Newtonian fluid", "du/dy", "Unit discharge", "Unit head", "HGL", "Reciprocating pump", "Piston acceleration", "Jet propulsion", "Kinematic viscosity", "Stokes", "Manning's formula"],42 "4. Environmental Engineering": ["BOD", "Aquatic life", "COD", "Water treatment", "Sewage", "Filtration", "Chlorination", "Ozone", "Sludge", "THMs", "Smog", "Fracking", "Alum", "Industrial effluents", "Chemical precipitation", "EIA", "Air Sparging", "Venturi Scrubber", "SCR", "Geologic sequestration", "Potable water", "pH", "TDS", "Pathogens", "Design period", "Carbon monoxide", "BOD test", "Permanent hardness", "Sullage", "Aerobic bacteria", "Freeman formula", "Soak pit", "Arsenic", "Grit chamber", "Trickling filter", "Skimming", "Primary pollutant", "Nitrate", "Landfill gas", "Sludge drying beds", "Constructed wetlands", "Pyrolysis", "FGD", "Leachate", "Factories Act 1948", "Activated carbon", "Adsorption", "Bar screen"],43 "5. Surveying": ["Theodolite", "Leveling", "Contouring", "Traverse", "Tachometry", "Stadia hairs", "EDM", "Total station", "Compass", "Metric chain", "Offset", "Plane table", "Direct plotting", "Pull correction", "HI", "Magnetic Declination", "Base line", "Cross-section", "RF", "Dip", "WCB to QB", "Prismatic compass", "Additive", "Multiplying constants", "1ยฐ Curve radius", "Latitude", "Face left", "Face right", "Trapezoidal volume", "Benchmark", "Level surface", "Backsighting", "Ranging", "Isogonic lines", "Working from whole to part", "Local attraction", "Reciprocal levelling", "Radiometric resolution", "Curvature correction", "Simpson's rule", "Optical square", "Overhanging cliff", "Tubular compass", "Stadia measurements", "Long chord", "PC to PT", "Surveyor's compass", "Cushing's level", "Transit axis", "Horizontal"],44 "6. Highway / Transportation Engineering": ["Sight distance", "Super elevation", "Camber", "Pavement", "Traffic", "Bitumen", "Tar", "Railway", "Parabolic camber", "Long rails", "Fishplate", "SSD", "Mean headway", "Ruling gradient", "Carriageway", "Brake reaction time", "Impact value", "CBR", "Viscosity", "Jayakar Committee", "Runway length", "Fatigue cracking", "Circular curve", "Greenshields", "Marshall Stability", "Ogee spillway", "PHF", "Vertical curve", "Spiral transition", "Max vehicle width", "2.44m", "OSD", "Highway drainage", "Westergaard assumption", "Vertical subgrade reaction", "Summit curve", "Off-tracking", "PIEV", "Asphalt mixes", "Cold", "Warm", "Hot", "Porous"],45 "7. Strength of Materials (SOM) / Mechanics of Structures": ["Stress", "Strain", "Bending moment", "Shear force", "Deflection", "Hooke's law", "Poisson's ratio", "Ductility", "Mild steel", "Strain energy", "P2L3/96EI", "Stiffness matrix", "Flexural rigidity", "Secant formula", "Eccentric loading", "Section modulus", "Volumetric strain", "Bulk Modulus", "Creep", "Propped cantilever", "Moment-area method", "Plastic moment capacity", "Plastic modulus", "Mohr's circle", "Max shear", "Flexibility method", "Buckling", "Fatigue", "Hoop stress", "Sphere PR/2t", "Shear stress distribution", "Zero at fibers", "Static indeterminacy", "m+r-3j", "Influence Line Diagram", "ILD", "Strain rosette", "Conjugate beam", "Zero force members"],46 "8. RCC (Reinforced Cement Concrete) / Prestressed Concrete": ["Limit state", "WSD", "Reinforcement", "Prestress", "Post-tensioning", "Stirrups", "Development length", "Inversely proportional to bond", "Modulus of Rupture", "Flexural test", "Exposure class", "Limiting moment", "Minimum tension steel", "Prevent buckling", "Static modulus", "Diagonal tension", "Side face reinforcement", "Retaining wall", "Seawater", "M30", "Ductile detailing", "Seismic Zone V", "Strong column-weak beam", "Torsional effect", "Shear wall", "Jacketing", "Retrofitting", "Floating columns", "Warping", "Dowel bars", "Under-reinforced", "Neutral axis 0.48d", "Fe415", "Pedestal", "L-beam", "Effective width", "Primary torsion", "Secondary torsion", "Modular ratio", "Es/Ec", "Chemical anchoring", "Epoxy"],47 "9. Steel Structures": ["Rivet", "Weld", "Bolt", "Tension member", "Compression member", "Plate girder", "Roof truss", "Plastic analysis", "Yield stress", "Block shear", "Slenderness ratio", "Pitch of rivets", "Fillet weld", "Gusset plate", "Load transfer", "Residual stresses", "Welding"],48 "10. Irrigation & Hydrology": ["Precipitation", "Runoff", "Hydrograph", "Duty", "Delta", "Crop period", "Spillway", "Canal", "Double-mass", "Sodic soil", "Drip", "Transpiration", "Unit Hydrograph", "Base flow", "Phi-index", "Mass curve", "Hydrologic cycle", "P=E+R", "Hyetograph", "Under-sluices", "Aqueduct", "Useful storage", "Water depth", "CCA", "Alluvial", "Non-alluvial channel", "Kor watering"],49 "11. Estimating, Costing & Valuation": ["Estimate", "BOQ", "Tender", "Contract", "Valuation", "Depreciation", "Salvage value", "Residual", "Rate analysis", "Centreline", "Carpet area", "Built-up area", "Super built-up area", "Loading", "Half brick wall", "Cement bag volume", "Dry volume", "Side slopes", "Pre-tender", "Revised estimate", "Plinth area", "Sunk cost", "Quantity surveying", "Capitalized value", "Net income", "YP"],50 "12. CPM / PERT (Construction Management)": ["Float", "Slack", "Critical path", "Event", "Activity", "Dummy", "Crashing", "PERT", "Te calculation", "Tail event", "Head event", "Objectives", "scope", "Scaffolding", "Total float", "Gantt chart", "Staircase", "3-12 steps", "Isometric projection", "0.816 scale factor", "View", "Controlling function", "WBS"]51}52# ====================================================================53 54# Google Sheets Setup55scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]56creds = ServiceAccountCredentials.from_json_keyfile_name("creds.json", scope)57client = gspread.authorize(creds)58 59# ๐ HAR GROUP KI ALAG RAM60QUIZ_STATE = {}61POLL_DATA = {}62 63def get_state(chat_id):64 if chat_id not in QUIZ_STATE:65 QUIZ_STATE[chat_id] = {'running': False, 'pending_results': [], 'pending_num_results': [], 'latest_paper': [], 'q_current': 0, 'q_total': 0, 'practice_finished_early': False}66 return QUIZ_STATE[chat_id]67 68def get_current_group(update: Update):69 chat_id_num = update.effective_chat.id70 chat_username = f"@{update.effective_chat.username}" if update.effective_chat.username else None71 if chat_id_num in GROUP_CONFIG: return chat_id_num72 if chat_username in GROUP_CONFIG: return chat_username73 return None74 75async def is_admin(update: Update, chat_id):76 if update.effective_user.id == MY_ADMIN_ID:77 return True78 try:79 user = await update.get_bot().get_chat_member(chat_id, update.effective_user.id)80 return user.status in ['creator', 'administrator'] or update.effective_user.username == "engineersatyaquiz"81 except:82 return False83 84async def is_paid_member(update: Update, context: ContextTypes.DEFAULT_TYPE):85 user_id = str(update.effective_user.id).strip()86 try:87 sheet = client.open_by_key(GROUP_CONFIG[PAID_GROUP])88 vip_sheet = sheet.worksheet("VIP_Members")89 data = vip_sheet.get_all_values()[1:]90 for row in data:91 if len(row) >= 6 and str(row[0]).strip() == user_id:92 status = str(row[5]).strip().lower()93 if status in ['active', 'free', 'permanent']:94 return True95 return False96 except Exception as e:97 print(f"VIP Sheet Check Error: {e}")98 return False99 100# --- 0. BOT START COMMAND ---101async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):102 if update.effective_chat.type == 'private':103 welcome_text = (104 "๐ <b>Welcome to EngineerSatya Official Bot!</b> ๐\n\n"105 "๐ฏ <b>Join our Daily practice sessions using /practice</b>\n\n"106 "๐ก <b>Important Note:</b>\n"107 "โ
If you are already a <b>VIP Paid Member</b>, this premium practice feature is completely <b>FREE</b> for you! Just type /practice to start.\n"108 "โ If you are <b>NOT</b> a paid member, you need to join our VIP Paid Group first to unlock these exclusive quizzes.\n\n"109 "๐ผ <b>Want to unlock premium questions and join the Paid Group?</b>\n"110 "Send a personal message to @Engineersatyaquiz ๐ฉ"111 )112 await update.message.reply_text(welcome_text, parse_mode='HTML')113 114# --- 1. RESULT SAVING ---115async def receive_quiz_answer(update: Update, context: ContextTypes.DEFAULT_TYPE):116 answer = update.poll_answer117 poll_info = POLL_DATA.get(answer.poll_id)118 if poll_info:119 chat_id = poll_info['chat_id']120 correct_id = poll_info['correct_id']121 is_num = poll_info.get('is_num', False)122 poll_date = poll_info.get('poll_date')123 user_name = f"{answer.user.first_name} {answer.user.last_name or ''}".strip()124 status = "Correct" if answer.option_ids[0] == correct_id else "Wrong"125 IST = pytz.timezone('Asia/Kolkata')126 now = datetime.now(IST)127 if is_num:128 if poll_date == now.date() and now.hour < 22:129 get_state(chat_id)['pending_num_results'].append([now.strftime("%d-%m-%Y %H:%M:%S"), answer.poll_id, user_name, status])130 else:131 get_state(chat_id)['pending_results'].append([str(time.ctime()), answer.poll_id, user_name, status])132 133# --- 2. ZIDDI LEADERBOARD ---134async def post_leaderboard(context: ContextTypes.DEFAULT_TYPE, chat_id):135 state = get_state(chat_id)136 max_retries = 5 137 for attempt in range(max_retries):138 try:139 sheet = client.open_by_key(GROUP_CONFIG[chat_id])140 res_sheet = sheet.worksheet("Results")141 if state['pending_results']:142 res_sheet.append_rows(state['pending_results'])143 state['pending_results'].clear()144 data = res_sheet.get_all_values()[1:]145 if not data:146 await context.bot.send_message(chat_id=chat_id, text="๐ง <b>Arey! Scoreboard khali hai. Pahale Quiz to chalao!!!</b>", parse_mode='HTML')147 return148 stats = {}149 for row in data:150 if len(row) > 3:151 name, status = row[2], row[3].strip()152 if name not in stats: stats[name] = {"correct": 0, "wrong": 0}153 if status == "Correct": stats[name]["correct"] += 1154 else: stats[name]["wrong"] += 1155 leaderboard_data = sorted([{"name": n, "net": (s["correct"] * 1) - (s["wrong"] * NEG_MARK), "total": s["correct"] + s["wrong"], "c": s["correct"], "w": s["wrong"]} for n, s in stats.items()], key=lambda x: x['net'], reverse=True)156 total_students = len(leaderboard_data)157 chunk_size = 20158 for chunk_start in range(0, total_students, chunk_size):159 chunk_end = min(chunk_start + chunk_size, total_students)160 chunk_data = leaderboard_data[chunk_start:chunk_end]161 if chunk_start == 0:162 msg = f"๐ <b>ENGINEERSATYA OFFICIAL RANKING</b> ๐\n๐ฅ <b>Total Participants: {total_students}</b>\nโโโโโโโโโโโโโโโโโโโโ\n"163 else:164 msg = f"๐ <b>RANKING CONTINUED ({chunk_start + 1} to {chunk_end})</b> ๐\nโโโโโโโโโโโโโโโโโโโโ\n"165 for idx, p in enumerate(chunk_data):166 real_rank = chunk_start + idx + 1167 rank_emoji = "๐ฅ" if real_rank == 1 else "๐ฅ" if real_rank == 2 else "๐ฅ" if real_rank == 3 else "๐"168 safe_name = str(p['name']).replace('<', 'ใ').replace('>', 'ใ').replace('&', 'and')169 msg += f"{rank_emoji} <b>{safe_name}</b> (Rank {real_rank}/{total_students})\nโ Total Attempt: {p['total']}\nโ Correct: {p['c']} (x1)\nโ Wrong: {p['w']} | Neg: -{p['w']*NEG_MARK:.2f}\nโ <b>NET SCORE: {p['net']:.2f} Marks</b>\n\n"170 if chunk_end == total_students:171 msg += "โโโโโโโโโโโโโโโโโโโโ\n๐ฅ <b>Consistency is the key to success! Analyze your mistakes and keep practicing to secure your selection!</b> ๐"172 await context.bot.send_message(chat_id=chat_id, text=msg, parse_mode='HTML')173 await asyncio.sleep(1)174 return175 except Exception as e:176 if attempt < max_retries - 1:177 await asyncio.sleep(10)178 else:179 try:180 await context.bot.send_message(chat_id=chat_id, text=f"โ ๏ธ <b>Leaderboard Error:</b> Server baar-baar fail ho raha hai. Thodi der baad `/Result_satya` command chala kar result nikal lijiye.", parse_mode='HTML')181 except: pass182 183# --- 3. AUTO WELCOME & PAID PROMO ---184async def welcome_new_member(update: Update, context: ContextTypes.DEFAULT_TYPE):185 chat_id = get_current_group(update)186 if not chat_id: return187 for member in update.message.new_chat_members:188 is_paid = chat_id == PAID_GROUP189 if is_paid:190 welcome_text = (191 f"๐ <b>Welcome to the VIP Engineersatya Squad, {member.first_name}!</b> ๐ทโโ๏ธ๐๏ธ\n\n"192 "You have officially unlocked the most premium practice platform for AE/JE Exams! Here is what you get as a VIP member:\n\n"193 "๐ฏ <b>VIP EXCLUSIVE FEATURES:</b>\n"194 "โ <b>Strict Negative Marking</b> to build exam-like accuracy.\n"195 "โฑ๏ธ <b>Variable Dynamic Timers</b> (More time for numericals, less for one-liners).\n"196 "๐ <b>Subject-Wise Quizzes</b> to target your weak areas.\n"197 "๐ฅ <b>Daily Top 20 Numericals</b> (Attempt anytime before 10:00 PM for the leaderboard).\n"198 "๐ <b>Updated Question Bank:</b> Access to all recent exam papers and questions up to 2026.\n"199 "๐ค <b>24/7 Personal Access:</b> Any quiz conducted in the group goes straight to the Official Bot for you to practice privately anytime!\n"200 "๐ <b>On-Demand PDF Tests:</b> Send any previous year paper PDF to @engineersatyaquiz, and we will convert it into a live bot quiz for you!\n\n"201 "๐ก <b>HOW TO PRACTICE PRIVATELY?</b>\n"202 "Go to our official bot DM and type:\n"203 "๐ <b>/satya_quiz_sets</b> (To see and attempt all previous VIP quizzes)\n\n"204 "<i>Let the real preparation begin. Your gateway to selection starts here!</i> ๐๐"205 )206 else:207 welcome_text = (208 f"๐ Welcome to <b>Engineersatya Quiz Group</b>, {member.first_name}! ๐ทโโ๏ธ๐๏ธ\n\n"209 "๐ <b>Group Rules:</b>\n1. No Spamming or Personal Chats. ๐ซ\n2. Focus on your AE/JE Exams. ๐ฏ\n3. Respect all members and admins. ๐ค\n\n"210 "๐ก <b>Want to boost your preparation?</b>\n"211 "You are warmly invited to join our <b>VIP Paid Group</b> to take your preparation to the next level (It's completely optional! ๐). Here is what you get:\n"212 "โ
<b>Strict Negative Marking</b> to build exam-like accuracy.\n"213 "โ
Daily Top 20 Numericals & Subject-wise Quizzes.\n"214 "โ
<b>Updated Question Bank</b> covering all recent exams up to 2026.\n"215 "โ
<b>Private Access:</b> Practice all previous quizzes anytime via Bot.\n\n"216 "๐ฉ <i>To join the VIP Squad, please send a direct message to: @Engineersatyaquiz</i>\n\n"217 "<b>Your Gateway to Selection starts here!</b> ๐๐"218 )219 await update.message.reply_text(welcome_text, parse_mode='HTML')220 221# ๐ 1. ADVANCE MEMORY WALA LIVE QUIZ LOOP 222async def run_quiz_loop(chat_id, context, final_list, set_name=""):223 state = get_state(chat_id)224 for i, item in enumerate(final_list):225 if not state['running']: break226 state['q_current'] += 1227 row = item['data']228 current_tab = item.get('tab', 'Satya_quiz')229 msg_kwargs = {'chat_id': chat_id}230 if chat_id == PAID_UP_GROUP:231 if current_tab == "UpGk":232 msg_kwargs['message_thread_id'] = 4 233 elif current_tab == "Computer":234 msg_kwargs['message_thread_id'] = 10 235 if i == 0 and current_tab == "UpGk":236 await context.bot.send_message(text="๐ข <b>[UP GK SECTION]</b>\nShuru ho rahe hain UP GK ke 20 shandaar sawal! ๐ฅ", parse_mode='HTML', **msg_kwargs)237 await asyncio.sleep(2)238 elif i > 0 and current_tab == "Computer" and final_list[i-1].get('tab', '') != "Computer":239 await context.bot.send_message(text="๐ป <b>[COMPUTER SECTION]</b>\nAb baari hai Computer ke sawalon ki! Taiyar ho jao! ๐", parse_mode='HTML', **msg_kwargs)240 await asyncio.sleep(2)241 try:242 raw_q = f"Q. {state['q_current']}/{state['q_total']} {row[0]}"243 max_q_len = 300 - len(BRANDING) - 5244 if len(raw_q) > max_q_len:245 full_msg = f"๐ <b>Q. {state['q_current']}/{state['q_total']} (Full Question):</b>\n{row[0]}"246 await context.bot.send_message(text=full_msg, parse_mode='HTML', **msg_kwargs)247 await asyncio.sleep(1)248 raw_q = f"Q. {state['q_current']}/{state['q_total']} Please read the full question sent above and answer below. ๐"249 q_text = raw_q + BRANDING250 251 # --- ๐ก๏ธ SMART DYNAMIC OPTIONS HANDLER ---252 raw_opts = [str(row[1]).strip(), str(row[2]).strip(), str(row[3]).strip(), str(row[4]).strip()]253 safe_options = [opt[:97] + "..." if len(opt) > 100 else opt for opt in raw_opts if opt != ""]254 if len(safe_options) < 2:255 safe_options.extend(["Option A (Empty)", "Option B (Empty)"][:2 - len(safe_options)])256 try:257 c_id = int(row[5])258 if c_id >= len(safe_options) or c_id < 0: c_id = 0259 except:260 c_id = 0261 # -----------------------------------------262 263 exp_text = str(row[6]) if len(row) > 6 and row[6] else "๐Eอnอgอiอnอeอeอrอsอaอtอyอaอ๐"264 if len(exp_text) > 200: exp_text = exp_text[:197] + "..."265 timer = int(row[7]) if len(row) > 7 and str(row[7]).isdigit() else 30266 image_url = str(row[8]).strip() if len(row) > 8 else ""267 if image_url.startswith("http"):268 try:269 await context.bot.send_photo(photo=image_url, **msg_kwargs)270 await asyncio.sleep(1)271 except: pass272 message = await context.bot.send_poll(273 question=q_text, options=safe_options, type=Poll.QUIZ,274 correct_option_id=c_id, explanation=exp_text,275 open_period=timer, is_anonymous=False, **msg_kwargs276 )277 POLL_DATA[message.poll.id] = {'chat_id': chat_id, 'correct_id': c_id}278 await asyncio.sleep(timer + 5)279 except Exception as e:280 try: await context.bot.send_message(chat_id=chat_id, text=f"โ ๏ธ <b>Network issue </b> Q.{i+1} skip ho gaya. Agla sawal aa raha hai...", parse_mode='HTML')281 except Exception: pass282 await asyncio.sleep(5)283 continue284 if state['running']:285 state['running'] = False286 await context.bot.send_message(chat_id=chat_id, text="๐ <b>Quiz Completed!</b> Calculating results... ๐ฅ๐ฅณ", parse_mode='HTML')287 await post_leaderboard(context, chat_id)288 289# --- 4. START GROUP QUIZ ---290async def start_quiz(update: Update, context: ContextTypes.DEFAULT_TYPE):291 await handle_quiz_start(update, context, is_resume=False)292 293# --- 4B. RESUME QUIZ COMMAND ---294async def resume_quiz(update: Update, context: ContextTypes.DEFAULT_TYPE):295 await handle_quiz_start(update, context, is_resume=True)296 297# ๐ 2. START QUIZ HANDLER298async def handle_quiz_start(update: Update, context: ContextTypes.DEFAULT_TYPE, is_resume):299 chat_id = get_current_group(update)300 if not chat_id:301 await update.message.reply_text("โ Ye group bot ke config mein added nahi hai!")302 return303 if not await is_admin(update, update.effective_chat.id):304 await update.message.reply_text("๐ซ <b>Sabar Karo!</b> Admin is the captain . ๐๐ฅ", parse_mode='HTML')305 return306 state = get_state(chat_id)307 if state['running']:308 await update.message.reply_text("โ ๏ธ <b>Quiz already chal raha hai! Thora wait kro ya pahle Stop kro</b>", parse_mode='HTML')309 return310 sheet = client.open_by_key(GROUP_CONFIG[chat_id])311 if not is_resume:312 try:313 res_sheet = sheet.worksheet("Results")314 res_sheet.clear()315 res_sheet.append_row(["Time", "Poll ID", "Name", "Status"])316 except Exception as e: pass317 state['pending_results'].clear()318 else:319 await update.message.reply_text("๐ <b>RESUME MODE ON!</b> Purane marks safe hain, yahin se aage shuru kar rahe hain...", parse_mode='HTML')320 state['running'] = True321 limit = DEFAULT_LIMIT322 tab_name = "Satya_quiz"323 set_name = ""324 if context.args:325 if context.args[0].isdigit():326 limit = int(context.args[0])327 if len(context.args) > 1:328 set_name = context.args[1]329 if len(context.args) > 2:330 tab_name = " ".join(context.args[2:])331 else:332 set_name = context.args[0]333 if len(context.args) > 1:334 tab_name = " ".join(context.args[1:])335 # ================= UP SPECIAL LOGIC =================336 if chat_id == PAID_UP_GROUP:337 await update.message.reply_text(f"๐งน <b>UP GK aur Computer ka Special Data nikal raha hoon... (Set: {set_name if set_name else 'None'})</b> โ๏ธ", parse_mode='HTML')338 try:339 upgk_sheet = sheet.worksheet("UpGk")340 comp_sheet = sheet.worksheet("Computer")341 except Exception:342 await update.message.reply_text("โ ๏ธ <b>Error:</b> Sheet mein 'UpGk' ya 'Computer' tab nahi mila! Spelling check karein.", parse_mode='HTML')343 state['running'] = False344 return345 upgk_data = upgk_sheet.get_all_values()346 upgk_pool = [{'row_num': idx, 'data': row, 'tab': 'UpGk'} for idx, row in enumerate(upgk_data, start=1) if idx > 1 and (row[11].strip() if len(row) > 11 else "") != "Sent"]347 random.shuffle(upgk_pool)348 final_list = upgk_pool[:20]349 comp_data = comp_sheet.get_all_values()350 comp_pool = [{'row_num': idx, 'data': row, 'tab': 'Computer'} for idx, row in enumerate(comp_data, start=1) if idx > 1 and (row[11].strip() if len(row) > 11 else "") != "Sent"]351 random.shuffle(comp_pool)352 final_list.extend(comp_pool[:15])353 if not final_list:354 await update.message.reply_text("โ ๏ธ In dono tabs mein naye sawal bache hi nahi hain!")355 state['running'] = False356 return357 if not is_resume: state['q_current'] = 0; state['q_total'] = len(final_list)358 else: state['q_total'] = state['q_current'] + len(final_list)359 360 # ๐ฅ JADOO: BULK UPDATE ADVANCE MEIN (WITH AI TAGGER) ๐ฅ361 if final_list:362 try:363 IST = pytz.timezone('Asia/Kolkata')364 today_str = datetime.now(IST).strftime("%d-%m-%Y")365 updates_by_tab = {}366 for item in final_list:367 tab = item.get('tab', 'Satya_quiz')368 row_num = item['row_num']369 row_data = item['data']370 if tab not in updates_by_tab: updates_by_tab[tab] = []371 372 # --- ๐ง AI SUBJECT DETECTOR ---373 existing_sub = str(row_data[9]).strip() if len(row_data) > 9 else ""374 if not existing_sub and tab not in ["UpGk", "Computer"]:375 full_text = f"{row_data[0]} {row_data[1]} {row_data[2]} {row_data[3]} {row_data[4]} {row_data[6] if len(row_data)>6 else ''}".lower()376 best_sub = "13. Civil Engg"377 max_matches = 0378 for sub_name, keywords in SUBJECT_KEYWORDS.items():379 matches = sum(1 for kw in keywords if kw.lower() in full_text)380 if matches > max_matches:381 max_matches = matches382 best_sub = sub_name383 updates_by_tab[tab].append({'range': f'J{row_num}', 'values': [[best_sub]]})384 # -------------------------------385 386 if set_name: updates_by_tab[tab].append({'range': f'L{row_num}:O{row_num}', 'values': [["Sent", today_str, set_name, today_str]]})387 else: updates_by_tab[tab].append({'range': f'L{row_num}:M{row_num}', 'values': [["Sent", today_str]]})388 for tab, updates in updates_by_tab.items():389 ws = sheet.worksheet(tab)390 ws.batch_update(updates)391 except Exception as e: print(f"Advance Batch Update Error: {e}")392 393 await update.message.reply_text(f"๐ <b>BOOM! [UP SPECIAL QUIZ] is LIVE!</b>\nTarget: <b>{state['q_total']}</b> Questions. ๐ช๐", parse_mode='HTML')394 asyncio.create_task(run_quiz_loop(chat_id, context, final_list, set_name))395 try:396 if not is_resume and set_name:397 log_sheet = sheet.worksheet("Command_Log")398 IST = pytz.timezone('Asia/Kolkata')399 time_str = datetime.now(IST).strftime("%d-%m-%Y %H:%M:%S")400 clean_set = set_name.lower().replace(' ', '')401 log_sheet.append_row([time_str, f"/practice_up_{clean_set}", len(final_list), set_name, "UP Special"])402 except Exception as e: pass403 return404 405 # ================= GENERAL QUIZ LOGIC =================406 await update.message.reply_text(f"๐งน <b>'{tab_name}' wali sheet se data nikal raha hoon... (Set: {set_name if set_name else 'None'})</b> โ๏ธ", parse_mode='HTML')407 try: quiz_sheet = sheet.worksheet(tab_name)408 except Exception:409 await update.message.reply_text(f"โ ๏ธ <b>Oops!</b> `{tab_name}` naam ka koi Panna (Tab) nahi mila.", parse_mode='HTML')410 state['running'] = False411 return412 data = quiz_sheet.get_all_values()413 quiz_pool = []414 for idx, row in enumerate(data, start=1):415 if idx == 1: continue416 if (row[11].strip() if len(row) > 11 else "") != "Sent":417 quiz_pool.append({'row_num': idx, 'data': row, 'tab': tab_name})418 if not quiz_pool:419 state['running'] = False420 await update.message.reply_text(f"โ ๏ธ <b>Arre!</b> '{tab_name}' wali sheet mein naye sawal bache hi nahi hain!", parse_mode='HTML')421 return422 random.shuffle(quiz_pool)423 final_list = quiz_pool[:limit]424 if chat_id == PAID_GROUP: state['latest_paper'] = [item['data'] for item in final_list]425 if not is_resume: state['q_current'] = 0; state['q_total'] = len(final_list)426 else: state['q_total'] = state['q_current'] + len(final_list)427 428 # ๐ฅ JADOO: BULK UPDATE ADVANCE MEIN (WITH AI TAGGER) ๐ฅ429 if final_list:430 try:431 IST = pytz.timezone('Asia/Kolkata')432 today_str = datetime.now(IST).strftime("%d-%m-%Y")433 updates_by_tab = {}434 for item in final_list:435 tab = item.get('tab', 'Satya_quiz')436 row_num = item['row_num']437 row_data = item['data']438 if tab not in updates_by_tab: updates_by_tab[tab] = []439 440 # --- ๐ง AI SUBJECT DETECTOR ---441 existing_sub = str(row_data[9]).strip() if len(row_data) > 9 else ""442 if not existing_sub and tab not in ["UpGk", "Computer"]:443 full_text = f"{row_data[0]} {row_data[1]} {row_data[2]} {row_data[3]} {row_data[4]} {row_data[6] if len(row_data)>6 else ''}".lower()444 best_sub = "13. Civil Engg"445 max_matches = 0446 for sub_name, keywords in SUBJECT_KEYWORDS.items():447 matches = sum(1 for kw in keywords if kw.lower() in full_text)448 if matches > max_matches:449 max_matches = matches450 best_sub = sub_name451 updates_by_tab[tab].append({'range': f'J{row_num}', 'values': [[best_sub]]})452 # -------------------------------453 454 if set_name: updates_by_tab[tab].append({'range': f'L{row_num}:O{row_num}', 'values': [["Sent", today_str, set_name, today_str]]})455 else: updates_by_tab[tab].append({'range': f'L{row_num}:M{row_num}', 'values': [["Sent", today_str]]})456 for tab, updates in updates_by_tab.items():457 ws = sheet.worksheet(tab)458 ws.batch_update(updates)459 except Exception as e: print(f"Advance Batch Update Error: {e}")460 461 await update.message.reply_text(f"๐ <b>BOOM! [{tab_name.upper()}] Quiz battle is LIVE!</b>\nTarget: <b>{state['q_total']}</b> Questions. ๐ช๐", parse_mode='HTML')462 asyncio.create_task(run_quiz_loop(chat_id, context, final_list, set_name))463 try:464 if not is_resume and set_name:465 log_sheet = sheet.worksheet("Command_Log")466 IST = pytz.timezone('Asia/Kolkata')467 time_str = datetime.now(IST).strftime("%d-%m-%Y %H:%M:%S")468 clean_set = set_name.lower() if set_name else 'general'469 log_sheet.append_row([time_str, f"/practice_{clean_set}_{limit}_{tab_name}", limit, set_name, tab_name])470 except Exception as e: pass471 472# --- 5. EMERGENCY STOP COMMAND ---473async def stop_quiz(update: Update, context: ContextTypes.DEFAULT_TYPE):474 chat_id = get_current_group(update)475 if not chat_id: return476 if not await is_admin(update, update.effective_chat.id):477 await update.message.reply_text("๐ซ <b>Sabar Karo!</b> Sirf admin roksakta hai.", parse_mode='HTML')478 return479 state = get_state(chat_id)480 if state['running']:481 state['running'] = False482 await update.message.reply_text("๐ <b>Emergency Brake Applied!</b> ๐จ\nAakhri sawal khatam hote hi Leaderboard aa jayega!", parse_mode='HTML')483 else:484 await update.message.reply_text("โน๏ธ <b>Relax!</b> Quiz pehle se hi ruki hui hai. ๐ด", parse_mode='HTML')485 486# --- 6. MANUAL RESULT COMMAND ---487async def manual_result(update: Update, context: ContextTypes.DEFAULT_TYPE):488 chat_id = get_current_group(update)489 if not chat_id: return490 if not await is_admin(update, update.effective_chat.id):491 await update.message.reply_text("๐ซ <b>Sabar Karo!</b> Admin is the captain . ๐๐ฅ", parse_mode='HTML')492 return493 await update.message.reply_text("๐ <b>Generating the Leaderboard... Please wait a moment!</b> โณ", parse_mode='HTML')494 await post_leaderboard(context, chat_id)495 496# ๐ NAYA FEATURE: Background Task Function for VIP Practice497async def run_practice_loop(chat_id, context, final_list, test_name, user_id, user_name):498 state = get_state(chat_id)499 state['pending_results'].clear()500 total_seconds = len(final_list) * 35501 minutes = total_seconds // 60502 seconds = total_seconds % 60503 time_text = f"{minutes} min {seconds} sec" if minutes > 0 else f"{seconds} seconds"504 for i, row in enumerate(final_list):505 try:506 raw_q = f"Q. {i+1}/{len(final_list)} {row[0]}"507 max_q_len = 300 - len(BRANDING) - 5508 if len(raw_q) > max_q_len:509 full_msg = f"๐ <b>Q. {i+1}/{len(final_list)} (Full Question):</b>\n{row[0]}"510 await context.bot.send_message(chat_id=chat_id, text=full_msg, parse_mode='HTML', protect_content=True)511 await asyncio.sleep(1)512 raw_q = f"Q. {i+1}/{len(final_list)} Please read the full question sent above and answer below. ๐"513 q_text = raw_q + BRANDING514 515 # --- ๐ก๏ธ SMART DYNAMIC OPTIONS HANDLER ---516 raw_opts = [str(row[1]).strip(), str(row[2]).strip(), str(row[3]).strip(), str(row[4]).strip()]517 safe_options = [opt[:97] + "..." if len(opt) > 100 else opt for opt in raw_opts if opt != ""]518 if len(safe_options) < 2:519 safe_options.extend(["Option A (Empty)", "Option B (Empty)"][:2 - len(safe_options)])520 try:521 c_id = int(row[5])522 if c_id >= len(safe_options) or c_id < 0: c_id = 0523 except:524 c_id = 0525 # -----------------------------------------526 527 exp_text = str(row[6]) if len(row) > 6 and row[6] else "Keep practicing! ๐"528 if len(exp_text) > 200: exp_text = exp_text[:197] + "..."529 calc_button = InlineKeyboardMarkup([530 [InlineKeyboardButton(text="๐ข Scientific Calculator", web_app=WebAppInfo(url="https://www.desmos.com/scientific"))]531 ])532 image_url = str(row[8]).strip() if len(row) > 8 else ""533 if image_url.startswith("http"):534 try:535 await context.bot.send_photo(chat_id=chat_id, photo=image_url)536 await asyncio.sleep(1)537 except Exception as e: pass538 message = await context.bot.send_poll(539 chat_id=chat_id,540 question=q_text,541 options=safe_options,542 type=Poll.QUIZ,543 correct_option_id=c_id,544 explanation=exp_text,545 is_anonymous=False,546 reply_markup=calc_button,547 protect_content=True548 )549 POLL_DATA[message.poll.id] = {'chat_id': chat_id, 'correct_id': c_id}550 await asyncio.sleep(1)551 except: continue552 553 await context.bot.send_message(554 chat_id=chat_id,555 text=f"๐ <b>VIP Paper bhej diya gaya hai!</b>\n\nโฑ๏ธ Aapke paas test attempt karne ke liye total <b>{time_text}</b> hain.\n\nโก <b>JALDI RESULT CHAHIYE?</b>\nAgar aapne paper poora kar liya aur time se pehle result dekhna chahte hain, toh yahan click karein ๐ /finish",556 parse_mode='HTML',557 protect_content=True558 )559 560 state['practice_finished_early'] = False561 for _ in range(total_seconds):562 if state.get('practice_finished_early'):563 break564 await asyncio.sleep(1)565 566 user_results = state['pending_results']567 if not user_results:568 await context.bot.send_message(chat_id=chat_id, text="โ ๏ธ <b>Time Up!</b> Lekin aapne ek bhi question attempt nahi kiya. Agli baar dhyan se time par test dein!", parse_mode='HTML', protect_content=True)569 return570 571 correct = 0572 wrong = 0573 subject_mistakes = {} 574 575 for i, res in enumerate(user_results):576 status = res[3]577 if status == "Correct": 578 correct += 1579 else: 580 wrong += 1581 try:582 if len(final_list[i]['data']) > 9:583 sub = final_list[i]['data'][9].strip()584 if not sub: sub = "Mixed/Unknown"585 else:586 sub = "Mixed/Unknown"587 except:588 sub = "Mixed/Unknown"589 590 if sub in subject_mistakes:591 subject_mistakes[sub] += 1592 else:593 subject_mistakes[sub] = 1594 595 net_score = (correct * 1) - (wrong * NEG_MARK)596 total_attempt = correct + wrong597 accuracy = (correct / total_attempt * 100) if total_attempt > 0 else 0598 599 if accuracy >= 80: remark = "๐ BEST (Top Ranker Level!)"600 elif accuracy >= 70: remark = "๐ BETTER (Strong Chances!)"601 elif accuracy >= 60: remark = "๐ GOOD (Keep Practicing!)"602 else: remark = "โ ๏ธ NEEDS IMPROVEMENT (Clear Your Concepts!)"603 604 scorecard = (605 "โฑ๏ธ <b>TIME UP!</b> โฑ๏ธ\n\n"606 "๐ <b>AAPKA PERSONAL SCORECARD</b> ๐\n"607 "โโโโโโโโโโโโโโโโโโโโ\n"608 f"๐ Total Questions: {len(final_list)}\n"609 f"๐ฏ Total Attempted: {total_attempt}\n"610 f"โ
Correct Answers: {correct} (x1 Mark)\n"611 f"โ Wrong Answers: {wrong}\n"612 f"โ ๏ธ Negative Marks: -{wrong * NEG_MARK:.2f}\n"613 "โโโโโโโโโโโโโโโโโโโโ\n"614 f"๐ <b>NET SCORE: {net_score:.2f} Marks</b>\n"615 f"๐งฎ <b>Accuracy: {accuracy:.2f}%</b>\n"616 f"๐ก <b>Remark: {remark}</b>\n\n"617 )618 619 if wrong > 0:620 scorecard += "๐ <b>Satya Sir's AI Analysis:</b>\n"621 for sub, count in subject_mistakes.items():622 if sub != "Mixed/Unknown":623 scorecard += f"โ ๏ธ Aapke <b>{sub}</b> ke <b>{count}</b> sawal galat hue hain.\n"624 scorecard += "\n๐ก <i>Advice: In subjects ko wapas revise karne ki sakht zaroorat hai!</i>\n\n"625 626 scorecard += "Success demands sacrifice! ๐ฅ Keep practicing, stay away from distractions, and focus purely on your goals! ๐ฏ"627 628 await context.bot.send_message(chat_id=chat_id, text=scorecard, parse_mode='HTML', protect_content=True)629 state['pending_results'].clear()630 try:631 IST = pytz.timezone('Asia/Kolkata')632 time_str = datetime.now(IST).strftime("%d-%m-%Y %H:%M:%S")633 neg_marks_str = f"{wrong * NEG_MARK:.2f}"634 net_score_str = f"{net_score:.2f}"635 accuracy_str = f"{accuracy:.2f}%"636 sheet = client.open_by_key(GROUP_CONFIG[PAID_GROUP])637 prac_sheet = sheet.worksheet("Practiceresult")638 prac_sheet.append_row([time_str, user_id, user_name, total_attempt, correct, wrong, neg_marks_str, net_score_str, test_name, accuracy_str, remark])639 except Exception as e:640 print(f"Practice Result Save Error: {e}")641 642# --- 7A. SECURE VIP PRACTICE MODE ---643async def practice_quiz(update: Update, context: ContextTypes.DEFAULT_TYPE):644 chat_id = update.effective_chat.id645 if update.effective_chat.type != 'private': return646 if not await is_paid_member(update, context):647 denied_msg = (648 "๐ซ <b>Access Denied!</b>\n\n"649 "๐ฌ๐ง You are not a VIP member yet! Please become a VIP member first by messaging in the Paid Group to unlock practice tests.\n\n"650 "๐ฎ๐ณ เคชเคนเคฒเฅ เคเคช เคชเฅเคก เคเฅเคฐเฅเคช (Paid Group) เคฎเฅเค เคฎเฅเคธเฅเค เคเคฐเคเฅ VIP เคฎเฅเคเคฌเคฐ เคฌเคจ เคเคพเค, เคเคธเคเฅ เคฌเคพเคฆ เคนเฅ เคเคช เคฏเคนเคพเค เคชเฅเคฐเฅเคเฅเคเคฟเคธ เคเคฐ เคชเคพเคเคเฅ!"651 )652 await context.bot.send_message(chat_id=chat_id, text=denied_msg, parse_mode='HTML')653 return654 text = update.message.text655 limit = DEFAULT_LIMIT656 tab_name = "Satya_quiz"657 req_set = ""658 if text.startswith('/practice_'):659 parts = text.split('_', 3)660 if len(parts) >= 4:661 req_set = parts[1].lower()662 if req_set == 'general': req_set = ""663 try: limit = int(parts[2])664 except: limit = DEFAULT_LIMIT665 tab_name = parts[3]666 else:667 words = text.split()668 command = words[0].lower()669 if "set" in command and command != "/practice":670 req_set = command.replace("/practice", "")671 args = words[1:]672 else:673 args = words[1:]674 if args and args[0].lower().startswith("set"):675 req_set = args[0]676 args = args[1:]677 if args:678 if args[0].isdigit():679 limit = int(args[0])680 if len(args) > 1: tab_name = " ".join(args[1:])681 else:682 tab_name = " ".join(args)683 set_msg = f" (Filtering for {req_set.upper()})" if req_set else " (Today's General Test)"684 await context.bot.send_message(chat_id=chat_id, text=f"๐ VIP Sheet ke `{tab_name}` se sawal dhoondh raha hoon...{set_msg}")685 try:686 paid_sheet = client.open_by_key(GROUP_CONFIG[PAID_GROUP])687 quiz_sheet = paid_sheet.worksheet(tab_name)688 data = quiz_sheet.get_all_values()[1:]689 sent_questions = []690 IST = pytz.timezone('Asia/Kolkata')691 today_str = datetime.now(IST).strftime("%d-%m-%Y")692 for row in data:693 is_sent = str(row[11]).strip() if len(row) > 11 else ""694 row_date = str(row[12]).strip() if len(row) > 12 else ""695 row_set = str(row[13]).strip() if len(row) > 13 else ""696 if is_sent == "Sent":697 if req_set:698 if req_set == "all": sent_questions.append(row)699 elif row_set.lower() == req_set.lower(): sent_questions.append(row)700 else:701 if row_date == today_str: sent_questions.append(row)702 if not sent_questions:703 if req_set:704 await context.bot.send_message(chat_id=chat_id, text=f"โ ๏ธ `{tab_name}` mein `{req_set}` ka koi test nahi mila!")705 else:706 await context.bot.send_message(chat_id=chat_id, text=f"โ ๏ธ Aaj `{tab_name}` mein koi naya general test nahi chalaya gaya hai!")707 return708 final_list = sent_questions[-limit:]709 await context.bot.send_message(chat_id=chat_id, text=f"๐ <b>Premium Practice Mode ON! [{tab_name.upper()}]</b>\nYe raha aapka VIP Paper ({len(final_list)} questions). Aaram se solve karo! ๐ช", parse_mode='HTML')710 user_id = str(update.effective_user.id)711 user_name = update.effective_user.first_name712 test_name = f"{tab_name} {req_set}".strip() if req_set else f"{tab_name} General"713 asyncio.create_task(run_practice_loop(chat_id, context, final_list, test_name, user_id, user_name))714 except Exception as e:715 await context.bot.send_message(chat_id=chat_id, text=f"โ ๏ธ <b>System Error!</b> `{str(e)[:150]}`", parse_mode='HTML')716 717# --- 7B. UP SPECIAL PRACTICE ---718async def practice_up_quiz(update: Update, context: ContextTypes.DEFAULT_TYPE):719 chat_id = update.effective_chat.id720 if update.effective_chat.type != 'private':721 await context.bot.send_message(chat_id=chat_id, text="โ ๏ธ Practice akele mein hoti hai. DM karo! ๐คซ")722 return723 if not await is_paid_member(update, context):724 denied_msg = "๐ซ <b>Access Denied!</b>\n๐ฌ๐ง You are not a VIP member yet!"725 await context.bot.send_message(chat_id=chat_id, text=denied_msg, parse_mode='HTML')726 return727 text = update.message.text728 req_set = text.replace('/practice_up_', '').strip().lower() if text.startswith('/practice_up_') else ""729 set_msg = f" (Filtering for SET: {req_set.upper()})" if req_set else " (Today's Test)"730 await context.bot.send_message(chat_id=chat_id, text=f"๐ <b>UP GK & Computer Tabs</b> khol raha hoon...{set_msg}", parse_mode='HTML')731 try:732 up_sheet = client.open_by_key(GROUP_CONFIG[PAID_UP_GROUP])733 try:734 upgk_sheet = up_sheet.worksheet("UpGk")735 comp_sheet = up_sheet.worksheet("Computer")736 except Exception:737 await context.bot.send_message(chat_id=chat_id, text="โ ๏ธ <b>Oops!</b> UP sheet mein 'UpGk' ya 'Computer' tab nahi mila.", parse_mode='HTML')738 return739 IST = pytz.timezone('Asia/Kolkata')740 today_str = datetime.now(IST).strftime("%d-%m-%Y")741 upgk_data = upgk_sheet.get_all_values()[1:]742 comp_data = comp_sheet.get_all_values()[1:]743 if req_set:744 upgk_sent = [row for row in upgk_data if (str(row[11]).strip() if len(row) > 11 else "") == "Sent" and (str(row[13]).strip().lower() if len(row) > 13 else "") == req_set]745 comp_sent = [row for row in comp_data if (str(row[11]).strip() if len(row) > 11 else "") == "Sent" and (str(row[13]).strip().lower() if len(row) > 13 else "") == req_set]746 else:747 upgk_sent = [row for row in upgk_data if (str(row[11]).strip() if len(row) > 11 else "") == "Sent" and (str(row[12]).strip() if len(row) > 12 else "") == today_str]748 comp_sent = [row for row in comp_data if (str(row[11]).strip() if len(row) > 11 else "") == "Sent" and (str(row[12]).strip() if len(row) > 12 else "") == today_str]749 upgk_final = upgk_sent[-20:] if len(upgk_sent) >= 20 else upgk_sent750 comp_final = comp_sent[-15:] if len(comp_sent) >= 15 else comp_sent751 final_list = upgk_final + comp_final752 if not final_list:753 await context.bot.send_message(chat_id=chat_id, text="โ ๏ธ Koi sawal nahi mila! Check kijiye Set Name sahi hai ya aaj koi test hua hai.")754 return755 await context.bot.send_message(chat_id=chat_id, text=f"๐ฏ <b>UP Special Practice Mode ON!</b>\nTotal <b>{len(final_list)}</b> Questions aa gaye! Phod do! ๐ช", parse_mode='HTML')756 user_id = str(update.effective_user.id)757 user_name = update.effective_user.first_name758 test_name = f"UP Special {req_set.upper()}" if req_set else "UP Special General"759 asyncio.create_task(run_practice_loop(chat_id, context, final_list, test_name, user_id, user_name))760 except Exception as e:761 await context.bot.send_message(chat_id=chat_id, text=f"โ ๏ธ <b>System Error!</b> `{str(e)[:150]}`", parse_mode='HTML')762 763# --- 8. RESTART BOT COMMAND ---764async def restart_bot(update: Update, context: ContextTypes.DEFAULT_TYPE):765 if not await is_admin(update, update.effective_chat.id):766 await update.message.reply_text("๐ซ Ruko jara....Sabar Karo! Admin is the captain . ๐๐ฅ")767 return768 await update.message.reply_text("โป๏ธ <b>Bot Restart ho raha hai...</b>", parse_mode='HTML')769 os.execl(sys.executable, sys.executable, *sys.argv)770 771# --- 9. MANUAL RESET RESULT COMMAND ---772async def reset_result(update: Update, context: ContextTypes.DEFAULT_TYPE):773 chat_id = get_current_group(update)774 if not chat_id: return775 if not await is_admin(update, update.effective_chat.id):776 await update.message.reply_text("๐ซ <b>Sabar Karo!</b> Sirf Admin hi scoreboard saaf kar sakta hai. ๐๐ฅ", parse_mode='HTML')777 return778 await update.message.reply_text("๐งน Scoreboard saaf kiya ja raha hai... Ek second rukiye! Clean India Green India!!!! โณ")779 try:780 state = get_state(chat_id)781 state['pending_results'].clear()782 sheet = client.open_by_key(GROUP_CONFIG[chat_id])783 res_sheet = sheet.worksheet("Results")784 res_sheet.clear()785 res_sheet.append_row(["Time", "Poll ID", "Name", "Status"])786 await update.message.reply_text("โ
<b>Lo Bhai! Scoreboard ekdum naya aur khali ho gaya.</b> Ab naye test ki taiyari karo! ๐", parse_mode='HTML')787 except Exception as e:788 await update.message.reply_text(f"โ ๏ธ <b>Error:</b> Sheet saaf karne mein dikkat aayi: {str(e)[:100]}", parse_mode='HTML')789 790# ---10. VIP MANAGEMENT COMMAND ---791async def add_vip(update: Update, context: ContextTypes.DEFAULT_TYPE):792 if not await is_admin(update, update.effective_chat.id): return793 if not update.message.reply_to_message:794 await update.message.reply_text("โ ๏ธ Jis student ko VIP banana hai, uske message par reply karke command dein.")795 return796 reply_msg = update.message.reply_to_message797 if reply_msg.from_user.id == context.bot.id and "๐" in reply_msg.text:798 match_id = re.search(r"๐\s*(\d+)", reply_msg.text)799 match_name = re.search(r"๐ค Name:\s*(.+)", reply_msg.text)800 user_id = match_id.group(1).strip() if match_id else None801 name = match_name.group(1).strip() if match_name else "Student"802 username = "No Username"803 else:804 user_id = str(reply_msg.from_user.id).strip()805 name = reply_msg.from_user.first_name806 username = f"@{reply_msg.from_user.username}" if reply_msg.from_user.username else "No Username"807 if not user_id:808 await update.message.reply_text("โ Error: Student ki ID nahi mil saki.")809 return810 args = context.args811 IST = pytz.timezone('Asia/Kolkata')812 join_date = datetime.now(IST)813 dur_str = "30"814 amount = "0"815 if len(args) == 2:816 dur_str = args[0]817 amount = args[1]818 elif len(args) == 3:819 try:820 join_date = datetime.strptime(args[0], "%d-%m-%Y")821 dur_str = args[1]822 amount = args[2]823 except:824 return await update.message.reply_text("โ ๏ธ Date format galat hai! DD-MM-YYYY use karein.")825 elif len(args) == 1:826 dur_str = args[0]827 else:828 return await update.message.reply_text("โ ๏ธ <b>Format:</b>\n`/addvip 30 100` (30 din, 100 Rs)\n`/addvip 15-03-2026 30 100` (Date, Din, Rs)", parse_mode='HTML')829 try:830 if dur_str.lower().endswith('m'): days = int(dur_str[:-1]) * 30831 else: days = int(dur_str)832 except: days = 30833 expiry_date = join_date + timedelta(days=days)834 join_str = join_date.strftime("%d-%m-%Y")835 expiry_str = expiry_date.strftime("%d-%m-%Y")836 await update.message.reply_text(f"โณ Processing... Payment: โน{amount}")837 try:838 sheet = client.open_by_key(GROUP_CONFIG[PAID_GROUP])839 vip_sheet = sheet.worksheet("VIP_Members")840 existing_data = vip_sheet.get_all_values()841 row_found = False842 for i, row in enumerate(existing_data):843 if i == 0: continue844 if len(row) > 0 and str(row[0]).strip() == user_id:845 vip_sheet.update_cell(i + 1, 4, join_str)846 vip_sheet.update_cell(i + 1, 5, expiry_str)847 vip_sheet.update_cell(i + 1, 6, "Active")848 vip_sheet.update_cell(i + 1, 7, amount)849 row_found = True850 break851 if not row_found:852 vip_sheet.append_row([user_id, name, username, join_str, expiry_str, "Active", amount])853 success_msg = (854 f"โ
<b>VIP ACCESS CONFIRMED!</b> ๐\n\n"855 f"๐ค <b>Name:</b> {name}\n"856 f"๐ฐ <b>Payment Received:</b> โน{amount}\n"857 f"๐
<b>Valid Till:</b> {expiry_str}\n"858 f"๐ข <b>Status:</b> Active\n\n"859 f"Student ko VIP groups join karne ke liye /sendlinks bhej dein! ๐"860 )861 await update.message.reply_text(success_msg, parse_mode='HTML')862 try:863 if update.effective_chat.type == 'private':864 await context.bot.send_message(chat_id=user_id, text=success_msg, parse_mode='HTML')865 await update.message.reply_text("๐ <i>Student ko notification DM me bhej diya gaya hai!</i>", parse_mode='HTML')866 except: pass867 except Exception as e:868 await update.message.reply_text(f"โ Error: {str(e)[:100]}", parse_mode='HTML')869 870# --- 11. VIP Rozana Reminder & 5th Day Kick Engine ---871async def check_vip_expiry(context: ContextTypes.DEFAULT_TYPE):872 IST = pytz.timezone('Asia/Kolkata')873 today = datetime.now(IST).date()874 try:875 sheet = client.open_by_key(GROUP_CONFIG[PAID_GROUP])876 vip_sheet = sheet.worksheet("VIP_Members")877 rows = vip_sheet.get_all_values()878 879 for i, row in enumerate(rows):880 if i == 0: continue881 if len(row) < 6: continue882 883 user_id = row[0]884 name = row[1]885 expiry_str = row[4]886 status = row[5]887 888 # In logon ko touch nahi karna889 if status.lower() in ["removed", "free", "permanent"]:890 continue891 892 try:893 expiry_date = datetime.strptime(expiry_str, "%d-%m-%Y").date()894 except ValueError:895 continue896 897 # Check kitne din hue expire hue898 days_passed = (today - expiry_date).days899 900 # ๐ข CASE 1: Expire hone wala hai ya aaj expire hua (Reminders for 4 days)901 if 0 <= days_passed <= 3:902 warning_number = days_passed + 1 # 1st, 2nd, 3rd, 4th Warning903 days_left_to_kick = 5 - warning_number904 905 msg = (906 f"โ ๏ธ <b>VIP Plan Expired! (Warning {warning_number}/5)</b>\n\n"907 f"Hello {name}, aapka VIP plan <b>{expiry_str}</b> ko expire ho gaya hai.\n"908 f"System aapko <b>{days_left_to_kick} din</b> baad auto-kick kar dega.\n\n"909 f"๐ Kripya aaj hi payment karke renew kar lein taki aapki practice na ruke!\n"910 f"๐ฐ UPI ID: <code>engineersatya@sbi</code>\n"911 f"๐ฉ Screenshot Satya Sir ko bhej dein."912 )913 try:914 await context.bot.send_message(chat_id=user_id, text=msg, parse_mode='HTML')915 except: pass916 917 # ๐ด CASE 2: Panchwa Din (Final Kick)918 elif days_passed >= 4:919 # Student ko final bye-bye message920 final_msg = (921 f"๐ซ <b>Final Alert! Access Terminated</b>\n\n"922 f"Alvida {name}! Aapne 5 din ke warning period mein plan renew nahi kiya.\n"923 f"Aapko VIP groups se nikal diya gaya hai. Re-join karne ke liye Satya Sir se sampark karein."924 )925 try:926 await context.bot.send_message(chat_id=user_id, text=final_msg, parse_mode='HTML')927 except: pass928 929 # Saare groups se kick karna930 private_groups = [PAID_GROUP, PAID_UP_GROUP, PAID_3RD_GROUP]931 for group_id in private_groups:932 try:933 await context.bot.ban_chat_member(chat_id=group_id, user_id=user_id)934 await context.bot.unban_chat_member(chat_id=group_id, user_id=user_id)935 except: pass936 937 # Sheet update karna938 vip_sheet.update_cell(i + 1, 6, "Removed")939 940 # Admin ko report dena941 try:942 await context.bot.send_message(943 chat_id=PAID_GROUP,944 text=f"๐ฅพ <b>Auto-Kick Done:</b> `{name}` ko plan expire hone ke 5 din baad nikal diya gaya hai.",945 parse_mode='HTML'946 )947 except: pass948 949 except Exception as e:950 print(f"VIP Check Error: {e}")951 952# ==========================================953# ๐ NEW FEATURE: WEEKLY NUMERICAL ENGINE954# ==========================================955async def auto_send_numericals(context: ContextTypes.DEFAULT_TYPE=None, update: Update=None):956 if update:957 chat_id = update.effective_chat.id958 if not await is_admin(update, chat_id):959 await update.message.reply_text("๐ซ Sirf admin numerical bhej sakta hai!")960 return961 bot_context = context962 else:963 chat_id = PAID_GROUP964 bot_context = context965 966 try:967 sheet = client.open_by_key(GROUP_CONFIG[chat_id])968 quiz_sheet = sheet.worksheet("Numerical_Satya")969 data = quiz_sheet.get_all_values()[1:]970 971 numerical_pool = []972 for idx, row in enumerate(data, start=2):973 num_sent = str(row[11]).strip() if len(row) > 11 else "" 974 if num_sent != "Sent" and row[0].strip() != "":975 numerical_pool.append({'row_num': idx, 'data': row})976 977 if not numerical_pool:978 msg = "โ ๏ธ <b>Admin Alert:</b> 'Numerical_Satya' tab mein naye sawal khatam ho gaye hain! Kripya sheet update karein."979 if update: await update.message.reply_text(msg, parse_mode='HTML')980 else: await bot_context.bot.send_message(chat_id=chat_id, text=msg, parse_mode='HTML')981 return982 983 random.shuffle(numerical_pool)984 final_list = numerical_pool[:20]985 total_nums = len(final_list)986 987 IST = pytz.timezone('Asia/Kolkata')988 today_str = datetime.now(IST).strftime("%A, %d %B %Y")989 today_date_only = datetime.now(IST).strftime("%d-%m-%Y")990 991 announcement = (992 f"๐ GOOD MORNING ENGINEERS! ๐\n\n"993 f"๐
Date: {today_str}\n"994 f"๐ TOP {total_nums} NUMERICALS OF THE DAY ๐\n\n"995 f"Aaj ke premium numericals bhej diye gaye hain. Pen-Copy uthaiye aur shuru ho jaiye! โ๏ธ\n"996 f"โณ Attempt Window: Raat 10:00 PM tak\n"997 f"๐ Final Rankings raat 10 baje publish ki jayengi! All the best! ๐ฏ๐"998 )999 1000 if update: await update.message.reply_text(announcement)1001 else: await bot_context.bot.send_message(chat_id=chat_id, text=announcement)1002 1003 await asyncio.sleep(5)1004 1005 for i, item in enumerate(final_list):1006 row_num = item['row_num']1007 row = item['data']1008 try:1009 raw_q = f"Q. {i+1}/{total_nums} {row[0]}"1010 max_q_len = 300 - len(BRANDING) - 51011 1012 if len(raw_q) > max_q_len:1013 full_msg = f"๐ <b>Q. {i+1}/{total_nums} (Full Question):</b>\n{row[0]}"1014 if update: await bot_context.bot.send_message(chat_id=chat_id, text=full_msg, parse_mode='HTML')1015 else: await bot_context.bot.send_message(chat_id=chat_id, text=full_msg, parse_mode='HTML')1016 await asyncio.sleep(1)1017 raw_q = f"Q. {i+1}/{total_nums} Please read the full question sent above and answer below. ๐"1018 1019 q_text = raw_q + BRANDING1020 1021 # --- ๐ก๏ธ SMART DYNAMIC OPTIONS HANDLER ---1022 raw_opts = [str(row[1]).strip(), str(row[2]).strip(), str(row[3]).strip(), str(row[4]).strip()]1023 safe_options = [opt[:97] + "..." if len(opt) > 100 else opt for opt in raw_opts if opt != ""]1024 if len(safe_options) < 2:1025 safe_options.extend(["Option 1 (Empty)", "Option 2 (Empty)"][:2 - len(safe_options)])1026 try:1027 c_id = int(row[5])1028 if c_id >= len(safe_options) or c_id < 0: c_id = 01029 except:1030 c_id = 01031 # -----------------------------------------1032 1033 exp_text = str(row[6]) if len(row) > 6 and row[6] else "๐Eอnอgอiอnอeอeอrอsอaอtอyอaอ๐"1034 if len(exp_text) > 200: exp_text = exp_text[:197] + "..."1035 1036 image_url = str(row[8]).strip() if len(row) > 8 else ""1037 if image_url.startswith("http"):1038 try:1039 if update: await bot_context.bot.send_photo(chat_id=chat_id, photo=image_url)1040 else: await bot_context.bot.send_photo(chat_id=chat_id, photo=image_url)1041 await asyncio.sleep(1)1042 except Exception as e: pass1043 1044 message = await bot_context.bot.send_poll(1045 chat_id=chat_id, question=q_text, options=safe_options, type=Poll.QUIZ,1046 correct_option_id=c_id, explanation=exp_text, is_anonymous=False,1047 )1048 1049 POLL_DATA[message.poll.id] = {'chat_id': chat_id, 'correct_id': c_id, 'is_num': True, 'poll_date': datetime.now(IST).date()}1050 1051 quiz_sheet.update_cell(row_num, 12, "Sent") 1052 quiz_sheet.update_cell(row_num, 13, today_date_only) 1053 1054 await asyncio.sleep(random.randint(5, 7))1055 1056 except Exception as e:1057 print(f"Numerical Send Error on Q{i+1}: {e}")1058 continue1059 1060 closing_msg = "โ
<b>BOOM! Aaj ke saare Numericals upload ho gaye hain!</b> ๐\n\nPhod do Engineers! Raat 10 baje Topper list mein naam chahiye! ๐"1061 if update: await update.message.reply_text(closing_msg, parse_mode='HTML')1062 else: await bot_context.bot.send_message(chat_id=chat_id, text=closing_msg, parse_mode='HTML')1063 1064 except Exception as e:1065 print(f"Auto-Numerical Major Error: {e}")1066 1067# ๐ NAYA FEATURE: DAILY TOP 10 NUMERICAL CHAMPIONS1068async def daily_num_result_trigger(context: ContextTypes.DEFAULT_TYPE):1069 chat_id = PAID_GROUP1070 try:1071 sheet = client.open_by_key(GROUP_CONFIG[chat_id])1072 res_sheet = sheet.worksheet("Numresult")1073 state = get_state(chat_id)1074 if state['pending_num_results']:1075 res_sheet.append_rows(state['pending_num_results'])1076 state['pending_num_results'].clear()1077 1078 data = res_sheet.get_all_values()[1:]1079 if not data: return1080 1081 stats = {}1082 for row in data:1083 if len(row) > 3:1084 name, status = row[2], row[3].strip()1085 if name not in stats: stats[name] = {"correct": 0, "wrong": 0}1086 if status == "Correct": stats[name]["correct"] += 11087 else: stats[name]["wrong"] += 11088 1089 leaderboard_data = sorted([{"name": n, "net": (s["correct"] * 1) - (s["wrong"] * NEG_MARK), "total": s["correct"] + s["wrong"], "c": s["correct"], "w": s["wrong"]} for n, s in stats.items()], key=lambda x: x['net'], reverse=True)1090 top_10 = leaderboard_data[:10]1091 1092 msg = f"๐ <b>TODAY'S TOP 10 NUMERICAL CHAMPIONS</b> ๐\nโโโโโโโโโโโโโโโโโโโโ\n\n"1093 for idx, p in enumerate(top_10):1094 rank_emoji = "๐ฅ" if idx == 0 else "๐ฅ" if idx == 1 else "๐ฅ" if idx == 2 else "๐"1095 safe_name = str(p['name']).replace('<', 'ใ').replace('>', 'ใ').replace('&', 'and')1096 msg += f"{rank_emoji} <b>{safe_name}</b>\nโ Attempt: {p['total']}\nโ Correct: {p['c']}\nโ Wrong: {p['w']}\nโ <b>SCORE: {p['net']:.2f} Marks</b>\n\n"1097 1098 msg += "โโโโโโโโโโโโโโโโโโโโ\n๐ฅ <b>Asli aur Final Result Sunday raat 10 baje aayega! Phodte raho!</b> ๐"1099 await context.bot.send_message(chat_id=chat_id, text=msg, parse_mode='HTML')1100 except Exception as e:1101 print(f"Daily Num Result Error: {e}")1102 1103# ๐ SUNDAY FINAL RANKING ENGINE1104async def auto_post_weekly_numerical_result(context: ContextTypes.DEFAULT_TYPE=None, update: Update=None):1105 is_manual = False1106 if update:1107 chat_id = update.effective_chat.id1108 if not await is_admin(update, chat_id):1109 await update.message.reply_text("๐ซ Bhai bhai bhai ruko Sirf admin result nikal sakta hai!")1110 return1111 bot_context = context1112 is_manual = True1113 else:1114 chat_id = PAID_GROUP1115 bot_context = context1116 1117 try:1118 sheet = client.open_by_key(GROUP_CONFIG[chat_id])1119 res_sheet = sheet.worksheet("Numresult")1120 1121 state = get_state(chat_id)1122 if state['pending_num_results']:1123 res_sheet.append_rows(state['pending_num_results'])1124 state['pending_num_results'].clear()1125 1126 data = res_sheet.get_all_values()[1:]1127 if not data:1128 msg = "๐ง <b>Abhi tak is hafte koi Numerical attempt nahi hua!</b>"1129 if update: await update.message.reply_text(msg, parse_mode='HTML')1130 else: await bot_context.bot.send_message(chat_id=chat_id, text=msg, parse_mode='HTML')1131 return1132 1133 stats = {}1134 for row in data:1135 if len(row) > 3:1136 name, status = row[2], row[3].strip()1137 if name not in stats: stats[name] = {"correct": 0, "wrong": 0}1138 if status == "Correct": stats[name]["correct"] += 11139 else: stats[name]["wrong"] += 11140 1141 leaderboard_data = sorted([{"name": n, "net": (s["correct"] * 1) - (s["wrong"] * NEG_MARK), "total": s["correct"] + s["wrong"], "c": s["correct"], "w": s["wrong"]} for n, s in stats.items()], key=lambda x: x['net'], reverse=True)1142 total_students = len(leaderboard_data)1143 chunk_size = 201144 1145 for chunk_start in range(0, total_students, chunk_size):1146 chunk_end = min(chunk_start + chunk_size, total_students)1147 chunk_data = leaderboard_data[chunk_start:chunk_end]1148 1149 if chunk_start == 0:1150 if is_manual:1151 msg = f"๐ <b>CURRENT NUMERICAL RANKING (MID-WEEK)</b> ๐\n๐ฅ <b>Total Participants: {total_students}</b>\nโโโโโโโโโโโโโโโโโโโโ\n"1152 else:1153 msg = f"๐ <b>FINAL WEEKLY NUMERICAL RANKING</b> ๐\n๐ฅ <b>Total Participants: {total_students}</b>\nโโโโโโโโโโโโโโโโโโโโ\n"1154 else:1155 msg = f"๐ <b>RANKING CONTINUED ({chunk_start + 1} to {chunk_end})</b> ๐\nโโโโโโโโโโโโโโโโโโโโ\n"1156 1157 for idx, p in enumerate(chunk_data):1158 real_rank = chunk_start + idx + 11159 rank_emoji = "๐ฅ" if real_rank == 1 else "๐ฅ" if real_rank == 2 else "๐ฅ" if real_rank == 3 else "๐"1160 safe_name = str(p['name']).replace('<', 'ใ').replace('>', 'ใ').replace('&', 'and')1161 msg += f"{rank_emoji} <b>{safe_name}</b> (Rank {real_rank}/{total_students})\nโ Total Attempt: {p['total']}\nโ Correct: {p['c']} (x1)\nโ Wrong: {p['w']} | Neg: -{p['w']*NEG_MARK:.2f}\nโ <b>NET SCORE: {p['net']:.2f} Marks</b>\n\n"1162 1163 if chunk_end == total_students:1164 if is_manual: msg += "โโโโโโโโโโโโโโโโโโโโ\n๐ฅ <b>Padhai shuru karo! Asli result Sunday ko aayega!</b> ๐"1165 else: msg += "โโโโโโโโโโโโโโโโโโโโ\n๐ฅ <b>Hafte ki mehnat yahan dikhti hai! Agle hafte fir se milte hain!</b> ๐"1166 1167 try:1168 if update and chunk_start == 0: await update.message.reply_text(msg, parse_mode='HTML')1169 else: await bot_context.bot.send_message(chat_id=chat_id, text=msg, parse_mode='HTML')1170 await asyncio.sleep(1)1171 except Exception as msg_e: pass1172 1173 if not is_manual:1174 res_sheet.clear()1175 res_sheet.append_row(["Time", "Poll ID", "Name", "Status"])1176 print("Sunday Automatic Sheet Cleared.")1177 1178 except Exception as e:1179 print(f"Weekly Numerical Result Error: {e}")1180 1181# ๐ NAYA FEATURE: DAILY PAID GROUP ADVERTISEMENT IN PUBLIC GROUP1182async def daily_paid_promo(context: ContextTypes.DEFAULT_TYPE):1183 chat_id = PUBLIC_GROUP1184 promo_msg = (1185 "๐ <b>BOOST YOUR PREPARATION WITH VIP SQUAD!</b> ๐\n\n"1186 "Are you serious about your AE/JE Exams? Join the <b>Engineersatya VIP Paid Group</b> and practice like a topper! ๐\n\n"1187 "๐ <b>WHAT YOU GET IN VIP BATCH:</b>\n"1188 "โ <b>Strict Negative Marking</b> to build exam-like accuracy.\n"1189 "โฑ๏ธ <b>Variable Dynamic Timers</b> (More time for numericals, less for one-liners).\n"1190 "๐ <b>Subject-Wise Quizzes</b> to target your weak areas.\n"1191 "๐ฅ <b>Daily Top 20 Numericals</b> (Attempt anytime before 10:00 PM for the leaderboard).\n"1192 "๐ <b>Latest 2026 Pattern Questions</b> strictly aligned with recent exams.\n"1193 "๐ค <b>Lifetime Personal Access:</b> Any quiz conducted in the group goes straight to the Official Bot for you to practice privately anytime!\n"1194 "๐ <b>On-Demand PDF Tests:</b> Send any previous year paper PDF to @engineersatyaquiz, and we will convert it into a live bot quiz for you!\n\n"1195 "๐ก <i>Don't just study, practice smartly!</i>\n"1196 "๐ฉ <b>DM @Engineersatyaquiz now to join the VIP Squad!</b>"1197 )1198 try:1199 await context.bot.send_message(chat_id=chat_id, text=promo_msg, parse_mode='HTML')1200 except Exception as e: pass