E-commerce Bot Example¶
A product catalog bot with shopping cart, pagination, and checkout using KeyboardFactory.
Tested Reference
For a fully tested, comprehensive example, see the Showcase Bot.
Overview¶
This example shows:
- Product catalog with pagination
- Shopping cart management (add/remove items)
- Checkout with
KeyboardFactory.create_confirmation_keyboard() - Error handling with
PyKeyboardError
Code¶
from pyrogram import Client, filters
from pykeyboard import (
InlineKeyboard,
InlineButton,
KeyboardFactory,
PyKeyboardError,
PaginationUnchangedError,
)
app = Client("ecommerce_bot")
# Mock product data
PRODUCTS = [
{"id": 1, "name": "Wireless Headphones", "price": 99.99, "category": "Electronics"},
{"id": 2, "name": "Smart Watch", "price": 199.99, "category": "Electronics"},
{"id": 3, "name": "Coffee Maker", "price": 79.99, "category": "Appliances"},
{"id": 4, "name": "Running Shoes", "price": 129.99, "category": "Sports"},
{"id": 5, "name": "Yoga Mat", "price": 39.99, "category": "Sports"},
{"id": 6, "name": "Cookbook", "price": 24.99, "category": "Books"},
]
PER_PAGE = 3
# User cart storage (in production, use a database)
user_carts: dict[int, list[dict]] = {}
def get_products_page(page: int) -> list[dict]:
"""Get products for a specific page."""
start = (page - 1) * PER_PAGE
return PRODUCTS[start : start + PER_PAGE]
def total_pages() -> int:
return (len(PRODUCTS) + PER_PAGE - 1) // PER_PAGE
def create_product_keyboard(products: list[dict], current_page: int) -> InlineKeyboard:
"""Create keyboard for product listing with pagination."""
keyboard = InlineKeyboard()
for product in products:
keyboard.row(
InlineButton(
f"{product['name']} - ${product['price']}",
callback_data=f"product:{product['id']}",
),
InlineButton("🛒 Add", callback_data=f"add_cart:{product['id']}"),
)
# Add pagination if needed
pages = total_pages()
if pages > 1:
try:
keyboard.paginate(pages, current_page, "page:{number}")
except PaginationUnchangedError:
pass # Same page — skip
# Cart and categories row
keyboard.row(
InlineButton("🛒 View Cart", callback_data="view_cart"),
InlineButton("📂 Categories", callback_data="categories"),
)
return keyboard
def create_cart_keyboard(cart_items: list[dict]) -> InlineKeyboard:
"""Create keyboard for cart management."""
keyboard = InlineKeyboard()
if not cart_items:
keyboard.add(InlineButton("🛍️ Continue Shopping", callback_data="page:1"))
return keyboard
total = sum(item["price"] * item["quantity"] for item in cart_items)
for item in cart_items:
subtotal = item["price"] * item["quantity"]
keyboard.row(
InlineButton(
f"{item['name']} x{item['quantity']} — ${subtotal:.2f}",
callback_data=f"cart_item:{item['id']}",
),
InlineButton("➕", callback_data=f"cart_add:{item['id']}"),
InlineButton("➖", callback_data=f"cart_remove:{item['id']}"),
)
keyboard.row(InlineButton(f"💰 Total: ${total:.2f}", callback_data="noop"))
keyboard.row(
InlineButton("✅ Checkout", callback_data="checkout"),
InlineButton("🛍️ Shop", callback_data="page:1"),
InlineButton("🗑️ Clear", callback_data="clear_cart"),
)
return keyboard
@app.on_message(filters.command("start"))
async def start_command(client, message):
user_carts[message.from_user.id] = []
products = get_products_page(1)
keyboard = create_product_keyboard(products, 1)
await message.reply_text(
"🛍️ **Welcome to our Store!**\n\n"
"Browse our products and add them to your cart:",
reply_markup=keyboard,
)
@app.on_callback_query(filters.regex(r"^page:"))
async def handle_pagination(client, callback_query):
page = int(callback_query.data.split(":")[1])
products = get_products_page(page)
pages = total_pages()
try:
keyboard = create_product_keyboard(products, page)
await callback_query.edit_message_text(
f"🛍️ **Products (Page {page}/{pages})**\n\n"
"Browse our products:",
reply_markup=keyboard,
)
except PyKeyboardError as e:
await callback_query.answer(f"Error: {e.error_code}")
await callback_query.answer()
@app.on_callback_query(filters.regex(r"^add_cart:"))
async def handle_add_to_cart(client, callback_query):
user_id = callback_query.from_user.id
product_id = int(callback_query.data.split(":")[1])
if user_id not in user_carts:
user_carts[user_id] = []
product = next((p for p in PRODUCTS if p["id"] == product_id), None)
if product:
cart_item = next(
(item for item in user_carts[user_id] if item["id"] == product_id),
None,
)
if cart_item:
cart_item["quantity"] += 1
else:
user_carts[user_id].append({**product, "quantity": 1})
await callback_query.answer(f"✅ {product['name']} added to cart!")
@app.on_callback_query(filters.regex(r"^view_cart$"))
async def handle_view_cart(client, callback_query):
user_id = callback_query.from_user.id
cart_items = user_carts.get(user_id, [])
keyboard = create_cart_keyboard(cart_items)
if not cart_items:
text = "🛒 **Your Cart**\n\nYour cart is empty."
else:
lines = [f"• {item['name']} x{item['quantity']} — "
f"${item['price'] * item['quantity']:.2f}"
for item in cart_items]
total = sum(item["price"] * item["quantity"] for item in cart_items)
text = "🛒 **Your Cart**\n\n" + "\n".join(lines) + f"\n\n**Total: ${total:.2f}**"
await callback_query.edit_message_text(text, reply_markup=keyboard)
await callback_query.answer()
@app.on_callback_query(filters.regex(r"^checkout$"))
async def handle_checkout(client, callback_query):
user_id = callback_query.from_user.id
cart_items = user_carts.get(user_id, [])
if not cart_items:
await callback_query.answer("Your cart is empty!")
return
total = sum(item["price"] * item["quantity"] for item in cart_items)
# Use KeyboardFactory for confirmation dialog
keyboard = KeyboardFactory.create_confirmation_keyboard(
yes_text="✅ Confirm Order",
no_text="❌ Cancel",
callback_pattern="order_{action}",
)
lines = [f"• {item['name']} x{item['quantity']} — "
f"${item['price'] * item['quantity']:.2f}"
for item in cart_items]
summary = "📋 **Order Summary**\n\n" + "\n".join(lines)
summary += f"\n\n**Total: ${total:.2f}**\n\nConfirm your order?"
await callback_query.edit_message_text(summary, reply_markup=keyboard)
await callback_query.answer()
@app.on_callback_query(filters.regex(r"^order_"))
async def handle_order_confirmation(client, callback_query):
action = callback_query.data.split("_")[1]
user_id = callback_query.from_user.id
if action == "yes":
cart_items = user_carts.get(user_id, [])
total = sum(item["price"] * item["quantity"] for item in cart_items)
user_carts[user_id] = []
back_kb = InlineKeyboard()
back_kb.add(InlineButton("🛍️ Continue Shopping", callback_data="page:1"))
await callback_query.edit_message_text(
f"✅ **Order Confirmed!**\n\n"
f"Total: ${total:.2f}\n\n"
"You will receive a confirmation email shortly.",
reply_markup=back_kb,
)
else:
keyboard = create_product_keyboard(get_products_page(1), 1)
await callback_query.edit_message_text(
"❌ Order cancelled.\n\nYou can continue shopping:",
reply_markup=keyboard,
)
await callback_query.answer()
if __name__ == "__main__":
app.run()
E-commerce Flow¶
graph LR
A[Browse Products] --> B[Add to Cart]
B --> C[View Cart]
C --> D[Checkout]
D --> E{Confirm?}
E -->|Yes| F[Order Confirmed]
E -->|No| A Features Demonstrated¶
- Product catalog with
paginate()andPaginationUnchangedErrorhandling - Shopping cart with add/remove buttons per item
KeyboardFactory.create_confirmation_keyboard()for checkout- Error handling with
PyKeyboardError
Running¶
pip install pykeyboard-kurigram
export TELEGRAM_BOT_TOKEN="..."
export TELEGRAM_API_ID="..."
export TELEGRAM_API_HASH="..."
python ecommerce_bot.py
Send /start to begin shopping.