import threading, logging, time, asyncio, os from typing import Optional from telegram import ForceReply, Update from telegram.error import Conflict, InvalidToken, NetworkError, RetryAfter, TimedOut from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters from telegram.request import HTTPXRequest from .cm_bot_hal import CM_BOT_HAL from .telegram_notifier import TelegramNotifier, get_telegram_bot_token logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logging.getLogger("httpx").setLevel(logging.WARNING) logger = logging.getLogger(__name__) creating_acc_now = False def _get_required_env(name: str) -> str: value = os.getenv(name) if value is None or value == "": raise RuntimeError(f"Missing required environment variable: {name}") return value def _get_env_float(name: str, default: float) -> float: try: return float(os.getenv(name, str(default))) except ValueError: return default def _get_env_int(name: str, default: int) -> int: try: return int(os.getenv(name, str(default))) except ValueError: return default def _retry_after_seconds(retry_after) -> int: if hasattr(retry_after, "total_seconds"): return max(1, int(retry_after.total_seconds())) return max(1, int(retry_after)) async def menu_cmd_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: menu = [ 'MENU', '/1 - Get Acc', '/2 - Set Security Pin', '/3 ', '/9 - Show Chat ID' ] await update.message.reply_text('\n'.join(menu)) async def get_acc_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: global creating_acc_now while creating_acc_now == True: await update.message.reply_text('CM account creation is running, queuing ...') await asyncio.sleep(1) creating_acc_now = True await update.message.reply_text('Start Getting CM Account ...') try: bot = CM_BOT_HAL() user = bot.get_user_api() msg = [ f'Username: {user["username"]}', f'Password: {user["password"]}', f'Link: {user["link"]}' ] await update.message.reply_text('\n'.join(msg)) except Exception as e: await update.message.reply_text(f'Error: {e}') finally: creating_acc_now = False async def set_security_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: if len(context.args) == 0 or len(context.args) > 1: await update.message.reply_text('CMD is wrong, please check and retry ...') return try: bot = CM_BOT_HAL() if bot.is_whatsapp_url(context.args[0]) == False: await update.message.reply_text('Link Format Wrong, please check and retry ...') return await update.message.reply_text('Start Setting Security Pin ...') result = bot.set_security_pin_api(context.args[0]) del bot await update.message.reply_text(f"Done setting Security Pin for {result['f_username']} - {result['t_username']} !") except Exception as e: await update.message.reply_text(f'Error: {e}') async def insert_to_user_table_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: if len(context.args) == 0 or len(context.args) != 2: await update.message.reply_text('CMD is wrong, please check and retry ...') return try: bot = CM_BOT_HAL() f_username, t_username = context.args security_pin = _get_required_env("CM_SECURITY_PIN") f_password = bot.get_user_pass_from_acc(f_username) if not f_password: raise Exception(f'Cannot find password for {f_username}') success = bot.insert_user_to_table_user( { 'f_username': f_username, 'f_password': f_password, 't_username': t_username, 't_password': security_pin } ) if success is False: raise Exception('Failed to insert user into table') await update.message.reply_text(f'Done insert {f_username} into user table.') except Exception as e: await update.message.reply_text(f'Error: {e}') async def show_chat_id_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: chat_id = update.effective_chat.id if update.effective_chat else "Unknown" await update.message.reply_text(f'Chat ID: {chat_id}') def monitor_amount_of_available_acc(notifier: Optional[TelegramNotifier] = None): global creating_acc_now max_available = 20 notifier = notifier or TelegramNotifier() while True: bot = None try: bot = CM_BOT_HAL() available_size = len(bot.get_all_available_acc()) if available_size <= max_available: creating_acc_now = True for i in range(available_size, max_available): try: bot.create_new_acc() except Exception as exc: err_text = str(exc) logger.exception("Failed to auto create CM account: %s", err_text) if notifier: if '[Fail login]' in err_text: notifier.notify_login_failure(err_text) else: notifier.notify_generic_error(err_text) break except Exception as exc: logger.exception("Unexpected error while monitoring accounts: %s", exc) if notifier: notifier.notify_generic_error(str(exc)) finally: creating_acc_now = False if bot is not None: del bot time.sleep(10 * 60) async def telegram_error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None: err = context.error if err is None: return if isinstance(err, Conflict): logger.warning( "Telegram polling conflict detected. Ensure only one bot instance runs with this token. %s", err, ) return if isinstance(err, RetryAfter): wait_seconds = _retry_after_seconds(err.retry_after) logger.warning("Telegram flood control exceeded. Retry after %s seconds.", wait_seconds) return if isinstance(err, (NetworkError, TimedOut)): logger.warning("Telegram network error: %s", err) return logger.exception("Unhandled Telegram update error", exc_info=err) def build_application(bot_token: str) -> Application: request_kwargs = dict( connect_timeout=_get_env_float('TELEGRAM_CONNECT_TIMEOUT', 10.0), read_timeout=_get_env_float('TELEGRAM_READ_TIMEOUT', 30.0), write_timeout=_get_env_float('TELEGRAM_WRITE_TIMEOUT', 30.0), pool_timeout=_get_env_float('TELEGRAM_POOL_TIMEOUT', 10.0), ) request = HTTPXRequest(**request_kwargs) get_updates_request = HTTPXRequest(**request_kwargs) application = ( Application.builder() .token(bot_token) .request(request) .get_updates_request(get_updates_request) .build() ) application.add_handler(CommandHandler("menu", menu_cmd_handler)) application.add_handler(CommandHandler("1", get_acc_handler)) application.add_handler(CommandHandler("2", set_security_handler)) application.add_handler(CommandHandler("3", insert_to_user_table_handler)) application.add_handler(CommandHandler("9", show_chat_id_handler)) application.add_error_handler(telegram_error_handler) # application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo)) return application def run_polling_forever(bot_token: str) -> None: retry_delay = max(1, _get_env_int('TELEGRAM_POLLING_RETRY_DELAY', 5)) max_retry_delay = max(retry_delay, _get_env_int('TELEGRAM_POLLING_MAX_RETRY_DELAY', 60)) bootstrap_retries = _get_env_int('TELEGRAM_BOOTSTRAP_RETRIES', 0) while True: logger.info( "Starting Telegram polling (bootstrap_retries=%s, long_poll_timeout=%s, retry_delay=%ss).", bootstrap_retries, _get_env_int('TELEGRAM_LONG_POLL_TIMEOUT', 30), retry_delay, ) application = build_application(bot_token) try: application.run_polling( allowed_updates=Update.ALL_TYPES, timeout=_get_env_int('TELEGRAM_LONG_POLL_TIMEOUT', 30), bootstrap_retries=bootstrap_retries, ) return except InvalidToken as exc: logger.exception( "Invalid Telegram bot token. Check TELEGRAM_BOT_TOKEN and restart after fixing it: %s", exc, ) raise except RetryAfter as exc: wait_seconds = _retry_after_seconds(exc.retry_after) logger.warning("Telegram flood control exceeded. Restarting polling in %s seconds.", wait_seconds) time.sleep(wait_seconds) retry_delay = min(wait_seconds, max_retry_delay) except Conflict as exc: logger.warning("Telegram polling conflict detected: %s", exc) logger.warning("Another bot instance is likely running with the same token. Retrying in %s seconds.", retry_delay) time.sleep(retry_delay) retry_delay = min(retry_delay * 2, max_retry_delay) except (NetworkError, TimedOut) as exc: logger.warning("Telegram network error while polling: %s", exc) logger.warning("Retrying polling in %s seconds.", retry_delay) time.sleep(retry_delay) retry_delay = min(retry_delay * 2, max_retry_delay) except Exception as exc: logger.exception("Unexpected polling crash: %s", exc) logger.warning("Retrying polling in %s seconds.", retry_delay) time.sleep(retry_delay) retry_delay = min(retry_delay * 2, max_retry_delay) def main() -> None: """Start the bot.""" bot_token = get_telegram_bot_token() # Start the Telegram bot print("Starting Telegram bot...") notifier = TelegramNotifier() threading.Thread(target=monitor_amount_of_available_acc, args=(notifier,), daemon=True).start() run_polling_forever(bot_token) if __name__ == "__main__": main()