Spaces:
Sleeping
Sleeping
Download auth_module/telegram_verify.py from moshabann/echo-tutor: direct link, hf CLI and curl.
- Browser
- Download file 18.5 kB
-
https://huggingface.co/spaces/moshabann/echo-tutor/resolve/main/auth_module/telegram_verify.py
- Command line
-
hf download hf://spaces/moshabann/echo-tutor/auth_module/telegram_verify.py
-
curl -L -o telegram_verify.py https://huggingface.co/spaces/moshabann/echo-tutor/resolve/main/auth_module/telegram_verify.py
18.5 kB
| """ | |
| ๐ฑ Auth Module โ Telegram Verification Bot | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| Inline-button driven verification flow with email + phone support. | |
| Usage: | |
| from auth_module.telegram_verify import create_verify_telegram_app | |
| telegram_app = create_verify_telegram_app() | |
| """ | |
| import os | |
| import re | |
| import random | |
| import logging | |
| from telegram import ( | |
| Update, | |
| ReplyKeyboardMarkup, | |
| KeyboardButton, | |
| InlineKeyboardButton, | |
| InlineKeyboardMarkup, | |
| ) | |
| from telegram.ext import ( | |
| Application, | |
| CommandHandler, | |
| MessageHandler, | |
| CallbackQueryHandler, | |
| filters, | |
| ContextTypes, | |
| ) | |
| from .config import auth_settings | |
| logger = logging.getLogger("auth_module.telegram") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ STATE | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| VERIFY_STATE: dict = {} | |
| # chat_id -> {"step": ..., "method": "email"|"phone", "email": ..., "user_id": ..., "otp": ...} | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โจ๏ธ KEYBOARDS | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def _main_keyboard(): | |
| return ReplyKeyboardMarkup( | |
| [[KeyboardButton("๐ ุชูุนูู ุงูุญุณุงุจ"), KeyboardButton("โน๏ธ ู ุณุงุนุฏุฉ")]], | |
| resize_keyboard=True, | |
| is_persistent=True, | |
| ) | |
| def _verify_method_keyboard(): | |
| return InlineKeyboardMarkup( | |
| [ | |
| [InlineKeyboardButton("๐ง ุจุงูุฅูู ูู", callback_data="verify_email")], | |
| [InlineKeyboardButton("๐ฑ ุจุฑูู ุงููุงุชู", callback_data="verify_phone")], | |
| [InlineKeyboardButton("โ ุฅูุบุงุก", callback_data="verify_cancel")], | |
| ] | |
| ) | |
| def _cancel_keyboard(): | |
| return InlineKeyboardMarkup( | |
| [ | |
| [InlineKeyboardButton("โ ุฅูุบุงุก ุงูุชูุนูู", callback_data="verify_cancel")], | |
| ] | |
| ) | |
| def _confirm_otp_keyboard(otp: str): | |
| return InlineKeyboardMarkup( | |
| [ | |
| [ | |
| InlineKeyboardButton( | |
| f"๐ ูุณุฎ ุงูููุฏ: {otp}", callback_data=f"copy_otp_{otp}" | |
| ) | |
| ], | |
| [ | |
| InlineKeyboardButton( | |
| f"โ ุชุฃููุฏ ุงูููุฏ ({otp})", callback_data=f"confirm_otp_{otp}" | |
| ) | |
| ], | |
| [InlineKeyboardButton("๐ ููุฏ ุฌุฏูุฏ", callback_data="resend_otp")], | |
| [InlineKeyboardButton("โ ุฅูุบุงุก", callback_data="verify_cancel")], | |
| ] | |
| ) | |
| def _phone_share_keyboard(): | |
| return ReplyKeyboardMarkup( | |
| [[KeyboardButton("๐ฑ ู ุดุงุฑูุฉ ุฑูู ุงููุงุชู", request_contact=True)]], | |
| resize_keyboard=True, | |
| one_time_keyboard=True, | |
| ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ก๏ธ SAFE REPLY | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def safe_reply(update: Update, text: str, reply_markup=None, parse_mode="HTML"): | |
| try: | |
| await update.message.reply_text( | |
| text, reply_markup=reply_markup, parse_mode=parse_mode | |
| ) | |
| except Exception as e: | |
| logger.warning(f"HTML reply failed: {e}") | |
| try: | |
| await update.message.reply_text(text, reply_markup=reply_markup) | |
| except Exception as e2: | |
| logger.error(f"Reply failed: {e2}") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ฏ COMMANDS | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| user_name = update.effective_user.first_name or "ู ุณุชุฎุฏู " | |
| msg = f"""โจ <b>ู ุฑุญุจุงู {user_name}!</b> | |
| ๐ ูุฐุง ุจูุช ุงูุชูุนูู ูู <b>RobovAI Nova</b>. | |
| ุงุถุบุท ุงูุฒุฑ ูุชูุนูู ุญุณุงุจู ๐""" | |
| inline_kb = InlineKeyboardMarkup( | |
| [ | |
| [ | |
| InlineKeyboardButton("๐ ุชูุนูู ุจุงูุฅูู ูู", callback_data="verify_email"), | |
| InlineKeyboardButton("๐ฑ ุชูุนูู ุจุงููุงุชู", callback_data="verify_phone"), | |
| ], | |
| ] | |
| ) | |
| await safe_reply(update, msg, reply_markup=_main_keyboard()) | |
| await update.message.reply_text( | |
| "โก ุงุฎุชุฑ ุทุฑููุฉ ุงูุชูุนูู:", parse_mode="HTML", reply_markup=inline_kb | |
| ) | |
| async def verify_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| msg = """๐ <b>ุชูุนูู ุงูุญุณุงุจ</b> | |
| ุงุฎุชุฑ ุทุฑููุฉ ุงูุชูุนูู: | |
| ๐ง <b>ุจุงูุฅูู ูู</b> โ ุฃุฏุฎู ุจุฑูุฏู ูุงุญุตู ุนูู ููุฏ | |
| ๐ฑ <b>ุจุฑูู ุงููุงุชู</b> โ ุดุงุฑู ุฑูู ู | |
| ุงุถุบุท ุงูุฒุฑ ุงูู ูุงุณุจ ๐""" | |
| await safe_reply(update, msg, reply_markup=_verify_method_keyboard()) | |
| async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| msg = """๐ <b>ููููุฉ ุงูุชูุนูู</b> | |
| 1๏ธโฃ ุณุฌูู ุญุณุงุจู ู ู ุงูู ููุน | |
| 2๏ธโฃ ุงุถุบุท <b>๐ ุชูุนูู ุงูุญุณุงุจ</b> | |
| 3๏ธโฃ ุงุฎุชุฑ ุงูุทุฑููุฉ (ุฅูู ูู ุฃู ูุงุชู) | |
| 4๏ธโฃ ุณุชุญุตู ุนูู ููุฏ โ ุงุถุบุท <b>ุชุฃููุฏ</b> | |
| 5๏ธโฃ ุชู ! ุณุฌูู ุฏุฎููู ู ู ุงูู ููุน โ """ | |
| await safe_reply(update, msg, reply_markup=_main_keyboard()) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ CALLBACK QUERY HANDLER | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| query = update.callback_query | |
| await query.answer() | |
| chat_id = str(query.message.chat_id) | |
| data = query.data | |
| if data == "verify_email": | |
| VERIFY_STATE[chat_id] = {"step": "awaiting_email", "method": "email"} | |
| await query.message.reply_text( | |
| "๐ง <b>ุฃุฏุฎู ุจุฑูุฏู ุงูุฅููุชุฑููู:</b>\n\n<i>ู ุซุงู: user@example.com</i>", | |
| parse_mode="HTML", | |
| reply_markup=_cancel_keyboard(), | |
| ) | |
| elif data == "verify_phone": | |
| VERIFY_STATE[chat_id] = {"step": "awaiting_phone", "method": "phone"} | |
| await query.message.reply_text( | |
| "๐ฑ ุงุถุบุท ุงูุฒุฑ ูู ุดุงุฑูุฉ ุฑูู ู ๐\n\n<i>ุฃู ุงูุชุจ ุจุฑูุฏู ุงูุฅููุชุฑููู</i>", | |
| parse_mode="HTML", | |
| reply_markup=_phone_share_keyboard(), | |
| ) | |
| elif data == "verify_cancel": | |
| VERIFY_STATE.pop(chat_id, None) | |
| await query.message.reply_text("โ ุชู ุงูุฅูุบุงุก.", reply_markup=_main_keyboard()) | |
| elif data.startswith("confirm_otp_"): | |
| otp_code = data.replace("confirm_otp_", "") | |
| state = VERIFY_STATE.get(chat_id) | |
| if ( | |
| state | |
| and state.get("step") == "awaiting_otp" | |
| and state.get("otp") == otp_code | |
| ): | |
| await _do_verify(query.message, chat_id, state, otp_code) | |
| else: | |
| await query.message.reply_text( | |
| "โ ๏ธ ุงูุชูุช ุงูุฌูุณุฉ. ุฃุนุฏ ุงูู ุญุงููุฉ ุจู /verify", reply_markup=_main_keyboard() | |
| ) | |
| elif data.startswith("copy_otp_"): | |
| otp_code = data.replace("copy_otp_", "") | |
| await query.message.reply_text( | |
| f"๐ <b>ุงูููุฏ:</b> <code>{otp_code}</code>\n๐ ุงุถุบุท ููุณุฎู", | |
| parse_mode="HTML", | |
| ) | |
| elif data == "resend_otp": | |
| state = VERIFY_STATE.get(chat_id) | |
| if state and state.get("user_id"): | |
| await _send_otp(query.message, chat_id, state) | |
| else: | |
| await query.message.reply_text( | |
| "โ ๏ธ ุงุจุฏุฃ ู ู ุฌุฏูุฏ ุจู /verify", reply_markup=_main_keyboard() | |
| ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ฒ CONTACT (Phone share) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def handle_contact(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| chat_id = str(update.effective_chat.id) | |
| state = VERIFY_STATE.get(chat_id) | |
| if not state or state.get("method") != "phone": | |
| return | |
| contact = update.message.contact | |
| phone = contact.phone_number if contact else None | |
| if not phone: | |
| await safe_reply(update, "โ ๏ธ ูู ุฃุชูู ุงูุฑูู . ุญุงูู ู ุฑุฉ ุฃุฎุฑู.") | |
| return | |
| phone = re.sub(r"[\s\-()]", "", phone) | |
| if not phone.startswith("+"): | |
| phone = "+" + phone | |
| try: | |
| from .database import auth_db | |
| user = await auth_db.get_user_by_telegram_or_phone(chat_id, phone) | |
| if not user: | |
| await safe_reply( | |
| update, | |
| f"โ ูู ูุชู ุงูุนุซูุฑ ุนูู ุญุณุงุจ ุจุฑูู <code>{phone}</code>\n\nุฌุฑูุจ ุจุงูุฅูู ูู ๐", | |
| reply_markup=_verify_method_keyboard(), | |
| ) | |
| VERIFY_STATE.pop(chat_id, None) | |
| return | |
| if user.get("is_verified"): | |
| await safe_reply( | |
| update, "โ ุญุณุงุจู ู ููุนูู ุจุงููุนู!", reply_markup=_main_keyboard() | |
| ) | |
| VERIFY_STATE.pop(chat_id, None) | |
| return | |
| state["user_id"] = user["id"] | |
| state["email"] = user.get("email", "") | |
| state["phone"] = phone | |
| VERIFY_STATE[chat_id] = state | |
| await _send_otp(update.message, chat_id, state) | |
| except Exception as e: | |
| logger.error(f"Phone verify error: {e}", exc_info=True) | |
| await safe_reply(update, "โ ุฎุทุฃ ุชููู. ุญุงูู ู ุฑุฉ ุฃุฎุฑู.") | |
| VERIFY_STATE.pop(chat_id, None) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ฌ TEXT MESSAGE HANDLER | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| chat_id = str(update.effective_chat.id) | |
| state = VERIFY_STATE.get(chat_id) | |
| message = (update.message.text or "").strip() | |
| # Menu buttons | |
| if message == "๐ ุชูุนูู ุงูุญุณุงุจ": | |
| await verify_command(update, context) | |
| return | |
| if message == "โน๏ธ ู ุณุงุนุฏุฉ": | |
| await help_command(update, context) | |
| return | |
| if not state: | |
| await safe_reply( | |
| update, "ุงุถุบุท ๐ <b>ุชูุนูู ุงูุญุณุงุจ</b> ููุจุฏุก", reply_markup=_main_keyboard() | |
| ) | |
| return | |
| # Cancel | |
| if message in ("ุงูุบุงุก", "ุฅูุบุงุก", "/cancel", "cancel"): | |
| VERIFY_STATE.pop(chat_id, None) | |
| await safe_reply(update, "โ ุชู ุงูุฅูุบุงุก.", reply_markup=_main_keyboard()) | |
| return | |
| # Awaiting email | |
| if state["step"] == "awaiting_email": | |
| email = message.lower() | |
| if "@" not in email or "." not in email: | |
| await safe_reply( | |
| update, | |
| "โ ๏ธ ุจุฑูุฏ ุบูุฑ ุตุญูุญ. ุญุงูู ู ุฑุฉ ุฃุฎุฑู:", | |
| reply_markup=_cancel_keyboard(), | |
| ) | |
| return | |
| try: | |
| from .database import auth_db | |
| user = await auth_db.get_user_by_email_unverified(email) | |
| if not user: | |
| await safe_reply( | |
| update, | |
| "โ ูุง ููุฌุฏ ุญุณุงุจ ุจูุฐุง ุงูุจุฑูุฏ. ุณุฌูู ู ู ุงูู ููุน ุฃููุงู.", | |
| reply_markup=_verify_method_keyboard(), | |
| ) | |
| VERIFY_STATE.pop(chat_id, None) | |
| return | |
| if user.get("is_verified"): | |
| await safe_reply( | |
| update, "โ ุญุณุงุจู ู ููุนูู ุจุงููุนู!", reply_markup=_main_keyboard() | |
| ) | |
| VERIFY_STATE.pop(chat_id, None) | |
| return | |
| state["user_id"] = user["id"] | |
| state["email"] = email | |
| VERIFY_STATE[chat_id] = state | |
| await _send_otp(update.message, chat_id, state) | |
| except Exception as e: | |
| logger.error(f"Email verify error: {e}", exc_info=True) | |
| await safe_reply(update, "โ ุฎุทุฃ ุชููู.") | |
| VERIFY_STATE.pop(chat_id, None) | |
| return | |
| # Awaiting phone but got text | |
| if state["step"] == "awaiting_phone": | |
| email = message.lower() | |
| if "@" in email and "." in email: | |
| state["step"] = "awaiting_email" | |
| state["method"] = "email" | |
| VERIFY_STATE[chat_id] = state | |
| await handle_message(update, context) | |
| else: | |
| await safe_reply( | |
| update, | |
| "๐ฑ ุงุถุบุท ุฒุฑ ู ุดุงุฑูุฉ ุงูุฑูู ุฃู ุงูุชุจ ุจุฑูุฏู.", | |
| reply_markup=_phone_share_keyboard(), | |
| ) | |
| return | |
| # Awaiting OTP (manual entry) | |
| if state["step"] == "awaiting_otp": | |
| code = message.strip() | |
| if not code.isdigit() or len(code) != 6: | |
| await safe_reply( | |
| update, | |
| "โ ๏ธ ุงูููุฏ 6 ุฃุฑูุงู . ุญุงูู ู ุฑุฉ ุฃุฎุฑู:", | |
| reply_markup=_cancel_keyboard(), | |
| ) | |
| return | |
| await _do_verify(update.message, chat_id, state, code) | |
| return | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ง HELPERS | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def _send_otp(message, chat_id: str, state: dict): | |
| try: | |
| from .database import auth_db | |
| otp = str(random.randint(100000, 999999)) | |
| await auth_db.store_otp(state["user_id"], otp, "telegram_verify", minutes=10) | |
| state["otp"] = otp | |
| state["step"] = "awaiting_otp" | |
| VERIFY_STATE[chat_id] = state | |
| msg = f"""โ <b>ุชู ุงูุนุซูุฑ ุนูู ุงูุญุณุงุจ!</b> | |
| ๐ง {state.get('email', '')} | |
| โโโโโโโโโโโโโโโโโโโโ | |
| ๐ <b>ููุฏ ุงูุชุญูู:</b> <code>{otp}</code> | |
| โฑ๏ธ ุตูุงุญูุฉ: <b>10 ุฏูุงุฆู</b> | |
| ุงุถุบุท <b>โ ุชุฃููุฏ</b> ููุชูุนูู ุงูููุฑู ๐""" | |
| await message.reply_text( | |
| msg, parse_mode="HTML", reply_markup=_confirm_otp_keyboard(otp) | |
| ) | |
| except Exception as e: | |
| logger.error(f"OTP error: {e}", exc_info=True) | |
| await message.reply_text("โ ุฎุทุฃ ุชููู.") | |
| VERIFY_STATE.pop(chat_id, None) | |
| async def _do_verify(message, chat_id: str, state: dict, code: str): | |
| try: | |
| from .database import auth_db | |
| valid = await auth_db.verify_otp(state["user_id"], code, "telegram_verify") | |
| if valid: | |
| await auth_db.set_user_verified(state["user_id"], telegram_chat_id=chat_id) | |
| VERIFY_STATE.pop(chat_id, None) | |
| await message.reply_text( | |
| "๐ <b>ุชู ุชูุนูู ุญุณุงุจู ุจูุฌุงุญ!</b>\n\nโ ุณุฌูู ุฏุฎููู ู ู ุงูู ููุน ุงูุขู.", | |
| parse_mode="HTML", | |
| reply_markup=_main_keyboard(), | |
| ) | |
| else: | |
| VERIFY_STATE.pop(chat_id, None) | |
| await message.reply_text( | |
| "โ ููุฏ ุบูุฑ ุตุญูุญ ุฃู ู ูุชูู. ุงุถุบุท /verify ููู ุญุงููุฉ ู ุฑุฉ ุฃุฎุฑู.", | |
| reply_markup=_main_keyboard(), | |
| ) | |
| except Exception as e: | |
| logger.error(f"Verify error: {e}", exc_info=True) | |
| await message.reply_text("โ ุฎุทุฃ ุชููู.") | |
| VERIFY_STATE.pop(chat_id, None) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๐ APP FACTORY | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def create_verify_telegram_app(token: str = None): | |
| """Create a standalone Telegram bot app for account verification.""" | |
| token = token or auth_settings.TELEGRAM_BOT_TOKEN | |
| if not token: | |
| logger.error("TELEGRAM_BOT_TOKEN not set") | |
| return None | |
| app = Application.builder().token(token).build() | |
| app.add_handler(CommandHandler("start", start_command)) | |
| app.add_handler(CommandHandler("verify", verify_command)) | |
| app.add_handler(CommandHandler("help", help_command)) | |
| app.add_handler(CallbackQueryHandler(handle_callback)) | |
| app.add_handler(MessageHandler(filters.CONTACT, handle_contact)) | |
| app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) | |
| logger.info("โ Verification Telegram app created") | |
| return app | |