Исходник captcha telegram bot

NGSH

Новичок
Автор темы
6
3

Что делает бот​

  • При команде /start показывает картинку-капчу.
  • Есть кнопка «Обновить капчу», чтобы получить новое изображение.
  • Пользователь вводит текст с картинки.
  • На ввод даётся три попытки.
  • Пока капча не решена, остальные команды не работают.
  • После правильного ввода бот снимает блокировку и принимает любые команды.


  • captcha telegram bot:
    import os, logging
    from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton, InputMediaPhoto
    from telegram.ext import (
        Application,
        CommandHandler,
        MessageHandler,
        CallbackContext,
        CallbackQueryHandler,
        filters
    )
    from captcha.image import ImageCaptcha
    
    TOKEN = "Сюда ты должен вставить свой токен бота"
    CAPTCHA_FOLDER = "captchas"
    os.makedirs(CAPTCHA_FOLDER, exist_ok=True)
    user_data = {}
    
    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
    logger = logging.getLogger(__name__)
    
    def generate_captcha(user_id: int):
        captcha_text = os.urandom(3).hex().upper()
        image = ImageCaptcha(width=300, height=150)
        file_path = os.path.join(CAPTCHA_FOLDER, f"{user_id}_captcha.png")
        image.write(captcha_text, file_path)
        return captcha_text, file_path
    
    async def start(update: Update, context: CallbackContext):
        user_id = update.effective_user.id
        captcha_text, captcha_path = generate_captcha(user_id)
        user_data[user_id] = {'captcha': captcha_text, 'passed': False, 'attempts': 0}
        keyboard = [[InlineKeyboardButton("Обновить капчу", callback_data='refresh')]]
        with open(captcha_path, 'rb') as photo:
            await update.message.reply_photo(
                photo=photo,
                caption="🔐 Для использования бота решите капчу:\n➖ Отправьте текст с картинки\n🔄 Если не видно — обновите капчу",
                reply_markup=InlineKeyboardMarkup(keyboard)
            )
    
    async def refresh_captcha(update: Update, context: CallbackContext):
        query = update.callback_query
        await query.answer()
        user_id = query.from_user.id
        captcha_text, captcha_path = generate_captcha(user_id)
        user_data[user_id] = {
            'captcha': captcha_text,
            'passed': False,
            'attempts': user_data.get(user_id, {}).get('attempts', 0)
        }
        keyboard = [[InlineKeyboardButton("Обновить капчу", callback_data='refresh')]]
        with open(captcha_path, 'rb') as photo:
            await query.edit_message_media(
                media=InputMediaPhoto(media=photo),
                reply_markup=InlineKeyboardMarkup(keyboard)
            )
            await query.edit_message_caption(
                caption="🔄 Капча обновлена! Отправьте текст с картинки:",
                reply_markup=InlineKeyboardMarkup(keyboard)
            )
    
    async def verify_captcha(update: Update, context: CallbackContext):
        user_id = update.message.from_user.id
        user_input = update.message.text.strip().upper()
        if user_data.get(user_id, {}).get('passed'):
            await update.message.reply_text("✅ Вы уже прошли проверку!")
            return
        if user_id not in user_data:
            await update.message.reply_text("⚠️ Сначала запустите /start")
            return
        captcha_data = user_data[user_id]
        if captcha_data['attempts'] >= 3:
            await update.message.reply_text("🚫 Превышено число попыток! Запустите /start заново")
            return
        if user_input == captcha_data['captcha']:
            user_data[user_id]['passed'] = True
            await update.message.reply_text(
                "✅ Капча пройдена успешно!\n\nТеперь вы можете использовать бота!\nПопробуйте команду: /hello"
            )
        else:
            user_data[user_id]['attempts'] += 1
            attempts_left = 3 - user_data[user_id]['attempts']
            await update.message.reply_text(
                f"❌ Неверно! Осталось попыток: {attempts_left}\nПопробуйте еще раз или обновите капчу"
            )
    
    async def hello(update: Update, context: CallbackContext):
        user_id = update.effective_user.id
        if user_data.get(user_id, {}).get('passed'):
            await update.message.reply_text(f"👋 Привет, {update.effective_user.first_name}!")
        else:
            await update.message.reply_text("🚫 Сначала пройдите капчу через /start")
    
    def main():
        application = Application.builder().token(TOKEN).build()
        application.add_handler(CommandHandler("start", start))
        application.add_handler(CommandHandler("hello", hello))
        application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, verify_captcha))
        application.add_handler(CallbackQueryHandler(refresh_captcha, pattern='^refresh$'))
        application.run_polling()
    
    if __name__ == '__main__':
        main()

1753823179935.png
 
Последнее редактирование:
  • Нравится
Реакции: minxty и Nichoo

Deps

Известный
181
122
Выглядит не плохо, но я бы доработал. Любой OCR сможет распознать контент капчи (не говоря уже об ИИ). Так же в 99% капчей работающих на текст-ввод - есть фаззи матчинг, а у тебя же его нету, из-за чего человек, который будет проходить капчу скажет "да идёт нахуй эта ебанная капча блять, я же "правильно ввёл"..."
Так же зачем лишние R/W операции на диск при создании капчи? Сразу в память её пиши и используй, а потом сноси.
1753900111823.png
 
  • Нравится
Реакции: NGSH

NGSH

Новичок
Автор темы
6
3
Теперь капча генерируется полностью в памяти, никаких временных файлов на диске — всё стало быстрее и чище.
Картинка капчи стала сложнее: добавил шум, линии, точки, блюр и разные шрифты. Теперь распознать её ботом или через OCR стало намного труднее.

Убрал все лишние операции с диском, теперь всё работает через оперативную память.

В целом код стал проще, быстрее и надёжнее.

1753959632595.png



captcha telegram bot::
import os
import logging
import random
import io
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton, InputMediaPhoto
from telegram.ext import (
    Application,
    CommandHandler,
    MessageHandler,
    CallbackContext,
    CallbackQueryHandler,
    filters
)

TOKEN = "Сюда ты должен вставить свой токен бота"
user_data = {}

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)

def generate_captcha(user_id: int):
    captcha_text = ''.join(random.choices('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', k=5))
    width, height = 220, 100
    image = Image.new('RGB', (width, height), (255, 255, 255))
    draw = ImageDraw.Draw(image)
    fonts = [
        ImageFont.truetype("arial.ttf", 48),
        ImageFont.truetype("arialbd.ttf", 48),
    ]
    for i, char in enumerate(captcha_text):
        font = random.choice(fonts)
        x = 20 + i * 35 + random.randint(-5, 5)
        y = 20 + random.randint(-10, 10)
        draw.text((x, y), char, font=font, fill=(random.randint(0, 120), random.randint(0, 120), random.randint(0, 120)))
    for _ in range(8):
        x1, y1 = random.randint(0, width), random.randint(0, height)
        x2, y2 = random.randint(0, width), random.randint(0, height)
        draw.line(((x1, y1), (x2, y2)), fill=(random.randint(100, 200), random.randint(100, 200), random.randint(100, 200)), width=2)
    for _ in range(300):
        x, y = random.randint(0, width), random.randint(0, height)
        draw.point((x, y), fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
    image = image.filter(ImageFilter.GaussianBlur(1.2))
    img_bytes = io.BytesIO()
    image.save(img_bytes, format='PNG')
    img_bytes.seek(0)
    return captcha_text, img_bytes

def fuzzy_match(a, b, max_distance=1):
    if len(a) != len(b):
        return False
    return sum(x != y for x, y in zip(a, b)) <= max_distance

async def start(update: Update, context: CallbackContext):
    user_id = update.effective_user.id
    captcha_text, captcha_img = generate_captcha(user_id)
    user_data[user_id] = {'captcha': captcha_text, 'passed': False, 'attempts': 0}
    keyboard = [[InlineKeyboardButton("Обновить капчу", callback_data='refresh')]]
    await update.message.reply_photo(
        photo=captcha_img,
        caption="🔐 Для использования бота решите капчу:\n➖ Отправьте текст с картинки\n🔄 Если не видно — обновите капчу",
        reply_markup=InlineKeyboardMarkup(keyboard)
    )

async def refresh_captcha(update: Update, context: CallbackContext):
    query = update.callback_query
    await query.answer()
    user_id = query.from_user.id
    captcha_text, captcha_img = generate_captcha(user_id)
    user_data[user_id] = {
        'captcha': captcha_text,
        'passed': False,
        'attempts': user_data.get(user_id, {}).get('attempts', 0)
    }
    keyboard = [[InlineKeyboardButton("Обновить капчу", callback_data='refresh')]]
    await query.edit_message_media(
        media=InputMediaPhoto(media=captcha_img),
        reply_markup=InlineKeyboardMarkup(keyboard)
    )
    await query.edit_message_caption(
        caption="🔄 Капча обновлена! Отправьте текст с картинки:",
        reply_markup=InlineKeyboardMarkup(keyboard)
    )

async def verify_captcha(update: Update, context: CallbackContext):
    user_id = update.message.from_user.id
    user_input = update.message.text.strip().upper()
    if user_data.get(user_id, {}).get('passed'):
        await update.message.reply_text("✅ Вы уже прошли проверку!")
        return
    if user_id not in user_data:
        await update.message.reply_text("⚠️ Сначала запустите /start")
        return
    captcha_data = user_data[user_id]
    if captcha_data['attempts'] >= 3:
        await update.message.reply_text("🚫 Превышено число попыток! Запустите /start заново")
        return
    if fuzzy_match(user_input, captcha_data['captcha']):
        user_data[user_id]['passed'] = True
        await update.message.reply_text(
            "✅ Капча пройдена успешно!\n\nТеперь вы можете использовать бота!\nПопробуйте команду: /hello"
        )
    else:
        user_data[user_id]['attempts'] += 1
        attempts_left = 3 - user_data[user_id]['attempts']
        await update.message.reply_text(
            f"❌ Неверно! Осталось попыток: {attempts_left}\nПопробуйте еще раз или обновите капчу"
        )

async def hello(update: Update, context: CallbackContext):
    user_id = update.effective_user.id
    if user_data.get(user_id, {}).get('passed'):
        await update.message.reply_text(f"👋 Привет, {update.effective_user.first_name}!")
    else:
        await update.message.reply_text("🚫 Сначала пройдите капчу через /start")

def main():
    application = Application.builder().token(TOKEN).build()
    application.add_handler(CommandHandler("start", start))
    application.add_handler(CommandHandler("hello", hello))
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, verify_captcha))
    application.add_handler(CallbackQueryHandler(refresh_captcha, pattern='^refresh$'))
    application.run_polling()

if __name__ == '__main__':
    main()
 
  • Нравится
Реакции: Deps