An intelligent Telegram bot for storing, organizing, and finding your memes using AI OCR and semantic search
MemeFinder transforms meme chaos into searchable order. Send memes to the bot, and it automatically extracts text using OCR, generates semantic descriptions using AI, and stores everything in a database with advanced full-text search capabilities.
MemeFinder is a Telegram bot that combines optical character recognition (OCR) and AI-powered image captioning to create a searchable meme library. The bot processes images sent to it, extracts text content, generates descriptive captions, and enables instant retrieval through Telegram's inline query interface using PostgreSQL trigram similarity search.
Key capabilities:
- Automatic text extraction and image descriptions via cloud vision APIs (Gemini, Anthropic, OpenAI)
- Fast semantic search powered by PostgreSQL trigram similarity
- S3-compatible storage for meme images
- Bulk archive processing for importing existing meme collections
- Organization through meme groups and storage chats
- Smart Image Processing: Cloud vision APIs (Gemini, Anthropic, OpenAI) for OCR and AI-generated descriptions
- Lightning-Fast Search: PostgreSQL trigram similarity search (
pg_trgm) with GIST indexing for fuzzy text matching - Inline Search: Search and share memes directly from any chat using Telegram inline queries
- Bulk Import: Process entire meme archives (ZIP files) with the
/archivecommand - S3 Storage: S3-compatible storage backend for reliable image persistence
- Organized Storage: Group memes with custom categories and separate storage chats
- Multi-Provider Vision: Swap between Gemini, Anthropic (Claude), or OpenAI with a config change
- Type-Safe: Full type hints with mypy enforcement
Using Docker (Recommended):
- Docker and Docker Compose
- Task (optional, for task automation)
- API key for a vision provider (Gemini, Anthropic, or OpenAI)
Local Development:
- Python 3.11+
- PostgreSQL 17
- S3-compatible storage (MinIO, AWS S3, etc.)
- API key for a vision provider
git clone https://gitlab.com/me1376/memefinder.git
cd memefinderCreate config/local.yaml:
token: "YOUR_TELEGRAM_BOT_TOKEN"
vision:
provider: gemini # Options: gemini, anthropic, openai
api_key: "YOUR_API_KEY"
model: null # Optional: Override default model
rate_limit: null # Optional: Requests per minute
updates_strategy: polling
database_dns: "postgresql+asyncpg://admin:admin@postgres:5432/memes"
storage:
endpoint: "minio:9000"
access_key: "minioadmin"
secret_key: "minioadmin"
bucket: "memes"
secure: false
public_url: nullConfiguration Options:
| Option | Type | Description | Example |
|---|---|---|---|
token |
string | Telegram bot token from @BotFather | "1234567890:ABCdefGHI..." |
database_dns |
string | PostgreSQL connection string (asyncpg format) | "postgresql+asyncpg://user:pass@host:5432/db" |
updates_strategy |
string | How to receive Telegram updates | "polling" or "webhook" |
vision.provider |
string | Vision API provider | "gemini", "anthropic", "openai" |
vision.api_key |
string | API key for the vision provider | "AIza..." |
vision.model |
string | Override default model (optional) | "gemini-2.0-flash", "claude-sonnet-4-20250514", "gpt-4o-mini" |
vision.rate_limit |
int | Requests per minute (optional) | 60 |
storage.endpoint |
string | S3-compatible endpoint | "minio:9000" or "s3.amazonaws.com" |
storage.access_key |
string | S3 access key | "minioadmin" |
storage.secret_key |
string | S3 secret key | "minioadmin" |
storage.bucket |
string | S3 bucket name | "memes" |
storage.secure |
bool | Use HTTPS for S3 | true or false |
storage.public_url |
string | Public URL prefix for images (optional) | "https://cdn.example.com" |
# Build the Docker image
docker compose build
# Start services (PostgreSQL + Bot)
docker compose upOR using Task:
# First time setup
task project_setup
# Run the bot with hot-reload
task botRun Alembic migrations to set up the database schema:
# Using Docker Compose
docker compose run --rm bot python -m bot alembic upgrade head
# OR using Task
task upgrade_dbThe initial migration automatically enables the PostgreSQL pg_trgm extension required for trigram similarity search.
-
Send a meme to the bot
- Bot uploads image to S3 storage
- Bot extracts text using OCR via cloud vision API
- Bot generates image description using AI via cloud vision API
- Automatically forwards to your configured storage chat
- Saves metadata (S3 URL, OCR text, description) to PostgreSQL
-
Search your memes
- Type
@your_bot_name search queryin any Telegram chat - Get instant fuzzy-matched results based on OCR text and AI descriptions
- Share directly to the conversation
- Type
/archive <zip_url>- Bulk import memes from a ZIP archive- Downloads and processes images in batches
- Rate-limited to comply with Telegram API restrictions
- Provides real-time progress updates
@memefinder_bot funny cat
Returns all memes with text or AI-generated descriptions matching "funny cat" using trigram similarity scoring.
- Bot Framework: aiogram 3.13.1 - Modern async Telegram bot framework
- Database: PostgreSQL 17 with
pg_trgmextension for trigram similarity - ORM: SQLAlchemy 2.0.35 (async mode with asyncpg driver)
- Vision APIs: Gemini, Anthropic (Claude), or OpenAI - OCR and image captioning
- Object Storage: S3-compatible storage (MinIO, AWS S3, etc.)
- Migrations: Alembic 1.13.3 - Database schema versioning
- Containerization: Docker
The database schema consists of four primary tables:
-
meme: Stores meme metadatas3_url- S3 storage URL for the imageocr- Extracted text from OCRimage_to_text- AI-generated description- Foreign keys to
chat(storage location) andmessage_id
-
chat: Represents Telegram chats- Stores source and storage chat configurations
- Many-to-many relationship between source and storage chats via
source__storagejunction table
-
memegroup: Organizes memes into collections- Many-to-many with
memeviamemegroup__memejunction table - Many-to-many with
chatviamemegroup__chatjunction table
- Many-to-many with
-
users: Telegram user accounts- Automatically created on first interaction
- Each user gets a default meme group on creation
The search system uses PostgreSQL's pg_trgm (trigram) extension for fuzzy text matching:
-
Index Creation: GIST index on concatenated OCR and AI description fields
CREATE INDEX ON meme USING gist((ocr || ' ' || image_to_text) gist_trgm_ops(siglen=32));
-
Query Strategy: Trigram distance operator (
<->>) for similarity scoringquery = select(Meme).where( (Meme.ocr + Meme.image_to_text).op('<->>')(search_text) < 0.4 ).order_by( (Meme.ocr + Meme.image_to_text).op('<->>')(search_text) )
-
Similarity Threshold: Configurable threshold (default: 0.6) controls match sensitivity
- Lower values = more fuzzy matches
- Higher values = stricter matching
-
Performance: GIST index enables sub-second searches across thousands of memes
- User sends photo to bot
- Image uploaded to S3 storage
- Vision API extracts text (OCR) and generates description
- Combined metadata stored in PostgreSQL with trigram index
- Photo forwarded to configured storage chat(s)
- Immediately searchable via inline queries
memefinder/
├── bot/
│ ├── bl/ # Business logic layer
│ │ ├── meme.py # Meme processing logic
│ │ └── user.py # User management
│ ├── controllers/ # Bot request handlers
│ │ ├── consumption/ # Meme ingestion handlers
│ │ │ ├── handlers.py # Photo consumption, archive processing
│ │ │ └── state.py # FSM states
│ │ └── search/ # Search functionality
│ │ └── handlers.py # Inline query search implementation
│ ├── db/ # Database layer
│ │ ├── models/ # SQLAlchemy ORM models
│ │ │ ├── meme.py # Meme model
│ │ │ ├── chat.py # Chat model
│ │ │ ├── memegroup.py # Meme group model
│ │ │ └── user.py # User model
│ │ ├── session.py # Async session management
│ │ └── types.py # Custom SQLAlchemy types
│ ├── middlewares/ # Aiogram middlewares
│ │ ├── session.py # DB session per request
│ │ └── user.py # User creation/update
│ ├── storage/ # Storage backends
│ │ ├── base.py # Storage interface
│ │ └── s3.py # S3-compatible storage
│ ├── vision/ # Vision API providers
│ │ ├── base.py # Provider interface
│ │ ├── factory.py # Provider factory
│ │ ├── prompt.py # Shared prompts for OCR/description
│ │ ├── anthropic.py # Anthropic (Claude) provider
│ │ ├── gemini.py # Google Gemini provider
│ │ └── openai.py # OpenAI provider
│ ├── utils/ # Utility functions
│ │ ├── setup.py # Config loading
│ │ └── tg.py # Telegram helpers
│ ├── config.py # Pydantic configuration models
│ ├── main.py # Bot entry point
│ └── cli.py # CLI commands
├── migrations/ # Alembic database migrations
│ └── versions/ # Migration scripts
├── config/ # Configuration files (gitignored)
│ └── local.yaml # Local configuration
├── requirements/
│ ├── prod.txt # Production dependencies
│ └── tools.txt # Development tools
├── Dockerfile # Docker build
├── docker-compose.yaml # Development environment
├── Taskfile.yaml # Task automation
└── alembic.ini # Alembic configuration
The project uses Task for automation. Available commands:
# Bot execution
task bot # Run bot with hot-reload (watchfiles)
# Interactive development
task ipython # IPython shell with bot context
# Code quality
task mypy # Type checking
task flake8 # Linting
task isort # Import sorting
task pre_commit # Run all pre-commit checks
# Database operations
task upgrade_db # Apply migrations
task downgrade_db # Rollback migrations
task make_migrations -- "description" # Create new migration
task check_db # Verify schema matches models
# Utilities
task bot_exec # Shell access to bot container
task setup_precommit_run_hook # Install git pre-commit hookCreate a migration:
task make_migrations -- "add new feature"This runs Alembic's autogenerate to detect schema changes.
Apply migrations:
task upgrade_dbApplies all pending migrations to bring database to latest version.
Rollback:
task downgrade_db # Rollback one migration
task downgrade_db -- base # Rollback all migrations- PEP 8 Compliance: Enforced by flake8
- Type Hints: Required on all functions (checked by mypy)
- Import Sorting: Managed by isort
- Async Patterns: All I/O operations use async/await
- SQLAlchemy 2.0: Use async session management with context vars
Before committing code, run:
task pre_commitThis executes isort, flake8, and mypy in sequence.
Install as git hook:
task setup_precommit_run_hookCreates .git/hooks/pre-commit that automatically runs checks before commits.
The docker-compose.yaml defines four services:
-
postgres: PostgreSQL 17 database
- Default credentials: admin/admin
- Database name: memes
- Port 8081 (mapped from 5432)
-
minio: S3-compatible object storage
- Default credentials: minioadmin/minioadmin
- API port: 9000
- Console port: 9001
-
bot: Main bot service
- Hot-reload with watchfiles
- Port 8080 (for webhook mode)
-
ipython: Interactive Python shell
- Same environment as bot
- Access to all bot modules
Contributions are welcome! To contribute:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make changes and test thoroughly
- Run pre-commit checks:
task pre_commit - Ensure all type checks pass:
task mypy - Commit with descriptive message:
git commit -m 'Add amazing feature' - Push to your fork:
git push origin feature/amazing-feature - Open a Pull Request
- Follow existing code style and patterns
- Add type hints to all new code
- Update tests if modifying business logic
- Document significant changes in commit messages
- Keep commits focused and atomic
This project is licensed under the MIT License - see below for details:
MIT License
Copyright (c) 2024 MemeFinder Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This project is built on top of excellent open-source technologies:
- aiogram - Modern async Telegram bot framework
- PostgreSQL - Advanced open-source database
- PostgreSQL
pg_trgmextension - Trigram-based text similarity search - SQLAlchemy - Python SQL toolkit and ORM
- MinIO - High-performance S3-compatible object storage
And cloud vision APIs:
- Google Gemini - Multimodal AI models
- Anthropic Claude - Advanced AI assistant
- OpenAI - GPT-4 Vision API