Skip to main content

ResponseWithBarcodeBot

@responseWithBarcodeBot is an automated Telegram bot designed to convert raw text payloads and URLs into Code 128 barcode graphics on the fly.

Architecture Overview

I designed @responseWithBarcodeBot as a decoupled component within my Telegram bot infrastructure. The architecture separates raw image rendering from event dispatching and payload validation.

+------------------+         +--------------------------+         +------------------------+
| Telegram Server | -----> | Handler & Auth Layer | -----> | Core Barcode Engine |
| (Webhook/Update) | | (handlers.py) | | (core.py) |
+------------------+ +--------------------------+ +------------------------+
| |
v v
[Length & Auth Checks] [In-Memory BytesIO PNG]

The system relies on two primary components:

  1. Core Rendering Engine (barcodebot/core.py): A pure Python module using python-barcode to construct high-density Code 128 images into in-memory byte streams.
  2. Telegram Interface (barcodebot/handlers.py): An event-driven dispatcher layer using python-telegram-bot to manage authorization, validate inputs, register command filters, and deliver photo updates.

Core Barcode Rendering Pipeline

To minimize disk I/O latency and simplify server maintenance, I opted against saving generated barcode images as temporary files. Instead, the rendering logic generates PNG graphics directly inside an io.BytesIO buffer.

import io
import logging
from typing import Optional
import barcode
from barcode.writer import ImageWriter

logger = logging.getLogger(__name__)

def generate_barcode(message: str) -> Optional[io.BytesIO]:
try:
writer_options = {
"write_text": False,
"background": "white",
"foreground": "black",
"module_width": 0.3,
"module_height": 15,
"format": "PNG"
}

barcode_obj = barcode.Code128(str(message), writer=ImageWriter())
buffer = io.BytesIO()
barcode_obj.write(buffer, options=writer_options)
buffer.seek(0)
return buffer

except Exception as e:
logger.error(f"Error generating barcode: {e}")
return None

Key Design Parameters

  • Code 128 Encoding: Selected for high data density and native support for alphanumeric ASCII payloads.
  • In-Memory Buffering: Writing to io.BytesIO allows the Telegram Bot API to read image streams directly without requiring temporary disk cleanups or file locking.
  • Custom Dimension Ratios: Setting module_width=0.3 and module_height=15 ensures standard scanner readability while keeping image payloads lightweight.

Request Validation and Authorization

In barcodebot/handlers.py, incoming text messages undergo validation before reaching the barcode rendering pipeline.

def parse_update_message(update) -> Optional[str]:
if not update.message.text:
return None

elif len(update.message.text) > int(settings.RESPONSEWITHBARCODEBOT_TEXT_LENGTH):
return None

else:
return update.message.text

Payload Constraints and Auth Filtering

  1. Payload Guardrails: Code 128 barcodes scale horizontally with text length. I capped text updates to 64 characters (RESPONSEWITHBARCODEBOT_TEXT_LENGTH) to avoid producing unresolvable, excessively wide barcodes on mobile device screens.
  2. Access Control: When settings.SERHIIXXX_TELEGRAM_ID is configured, the handler verifies the incoming Telegram user ID against the administrative whitelist before proceeding with image rendering.
  3. Lifecycle Management: Event listeners track bot membership updates using ChatMemberHandler to handle user departures cleanly.

Telegram Handler Registration

Handlers are bound to the python-telegram-bot dispatcher through register_responsewithbarcodebot_handlers:

def register_responsewithbarcodebot_handlers(dispatcher):
dispatcher.add_handler(CommandHandler('start', handle_start_message))
dispatcher.add_handler(MessageHandler(Filters.text, handle_message))
dispatcher.add_handler(ChatMemberHandler(user_left, ChatMemberHandler.MY_CHAT_MEMBER))

When a user transmits text, the bot acknowledges the request, generates the image buffer via generate_barcode(), and sends the image back as a photo reply using update.message.reply_photo(buffer, caption=...).