Skip to main content

Minimal emoji captcha middleware for aiogram v3

Project description

AioCaptcha (aiogram middleware)

Minimal captcha middleware for aiogram v3.

Behavior:

  • Any user interaction with the bot is blocked until the user passes the captcha.
  • Captcha is an inline keyboard with emojis. The bot asks the user to click the required emoji.
  • Successful users can be stored persistently (SQLite) so they never need to pass captcha again.

Requirements

  • Python 3.10+ (recommended)
  • aiogram>=3.0.0

Install

pip install aiocaptcha

Quick start

Create a bot file like Source/main.py and run it.

1) Set the token

export BOT_TOKEN="YOUR_TELEGRAM_BOT_TOKEN"

2) Run

python Source/main.py

Usage

Minimal (in-memory)

Users will be considered “verified” only until the bot restarts.

from aiogram import Bot, Dispatcher
from aiocaptcha import CaptchaManager

bot = Bot(token="...")
dp = Dispatcher()

captcha = CaptchaManager()
captcha.setup(dp)

# ... register your handlers
await dp.start_polling(bot)
Persistent (SQLite)

Users who passed captcha will be stored in captcha.sqlite3 and won’t be asked again after restarts.

from aiogram import Bot, Dispatcher
from aiocaptcha import CaptchaManager, SqliteCaptchaStorage

bot = Bot(token="...")
dp = Dispatcher()

storage = SqliteCaptchaStorage("captcha.sqlite3")
captcha = CaptchaManager(storage=storage)
captcha.setup(dp)

await dp.start_polling(bot)

Note: SqliteCaptchaStorage uses a single connection with a lock and is fine for typical small bots.

Custom storage (your own)

You can provide your own storage to persist “verified” users.

The required interface is:

from typing import Protocol


class CaptchaStorage(Protocol):
  def is_verified(self, user_id: int) -> bool: ...

  def mark_verified(self, user_id: int) -> None: ...

  def reset(self, user_id: int) -> None: ...

Example: a simple file-backed JSON storage (minimal example):

import json
from pathlib import Path
from typing import Set

from aiocaptcha import CaptchaManager, CaptchaOptions


class JsonCaptchaStorage:
  def __init__(self, path: str = "verified.json") -> None:
    self.path = Path(path)
    self._verified: Set[int] = set()
    self._load()

  def _load(self) -> None:
    if not self.path.exists():
      return
    data = json.loads(self.path.read_text("utf-8"))
    self._verified = set(int(x) for x in data.get("verified", []))

  def _save(self) -> None:
    self.path.write_text(
      json.dumps({"verified": sorted(self._verified)}, ensure_ascii=False),
      "utf-8",
    )

  def is_verified(self, user_id: int) -> bool:
    return user_id in self._verified

  def mark_verified(self, user_id: int) -> None:
    self._verified.add(user_id)
    self._save()

  def reset(self, user_id: int) -> None:
    self._verified.discard(user_id)
    self._save()


opts = CaptchaOptions()
storage = JsonCaptchaStorage("verified.json")
captcha = CaptchaManager(options=opts, storage=storage)

For production/multi-instance setups, a Redis-based storage is usually a better fit.

Configuration

CaptchaOptions

Use CaptchaOptions to customize messages and emoji pool:

from aiocaptcha import CaptchaManager, CaptchaOptions

opts = CaptchaOptions(
    prompt_text="Please verify you're human by clicking {target}",
    passed_text="Captcha passed successfully! ✅",
    wrong_alert="Incorrect choice. Please try again.",
    pool=("🐶", "🐱", "🐭", "🐹", "🐰", "🦊", "🐻", "🐼"),
    choices=4,
    ttl_seconds=120,
)

captcha = CaptchaManager(options=opts)

Available options

  • callback_prefix — prefix for callback data (default: aiocaptcha)
  • pool — tuple of emojis
  • choices — how many buttons are shown
  • ttl_seconds — challenge expiration time
  • prompt_text — message template. Use {target}
  • passed_text — message after passing
  • wrong_alert — alert text on wrong click

How it works

Implementation notes
  • The library attaches:
    • an update-level middleware (dp.update.middleware(...)) that blocks all updates from non-verified users;
    • a small router that handles captcha callback queries.
  • When a non-verified user sends any message/clicks any button, the bot sends (or edits) a captcha message.
  • On correct click the user becomes verified; on wrong click the captcha regenerates.

Logging

Events and setup

The library logs events using Python logging under the logger name aiocaptcha:

  • captcha_passed user_id=...
  • captcha_failed user_id=... answer=... expected=...
  • captcha_sent user_id=... chat_id=... message_id=...

Enable logging in your app:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)

Notes / limitations

Notes
  • Captcha state and verification are keyed by user_id.
  • In groups, a captcha message is sent to the chat where the interaction happened.
  • If you run multiple bot instances, use a shared storage (SQLite on shared disk, or implement your own storage like Redis).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiocaptcha-0.1.0.tar.gz (9.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiocaptcha-0.1.0-py3-none-any.whl (8.8 kB view details)

Uploaded Python 3

File details

Details for the file aiocaptcha-0.1.0.tar.gz.

File metadata

  • Download URL: aiocaptcha-0.1.0.tar.gz
  • Upload date:
  • Size: 9.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for aiocaptcha-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b5290c8cdd366578223db20d17f27228aefeff235248b0127c0406abc8ca44d9
MD5 5c4512c8b7aedff317b1b4fb327eb594
BLAKE2b-256 4e19a91d748ad222a6e39a8e3669e8fc6111ba0cdc8b6e66e2da4fe541053227

See more details on using hashes here.

File details

Details for the file aiocaptcha-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: aiocaptcha-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for aiocaptcha-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6770d67e438c01862e0a99756fa91ce12d6d6c9cffb8909931d91ac74f758900
MD5 c5b8e2f919de63b9fea07132d57c342b
BLAKE2b-256 43e1f72a932ce01debd5f90d869611027caa40df3059fab66137eb9aa9e0658c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page