رفتن به محتوای اصلی
☕ سطح: متوسط · رایگان جدید

سورس ربات منوی کافه و سفارش از میز (بله)

مشتری منو را با دکمه می‌بیند، آیتم‌ها را به سبد اضافه می‌کند، شمارهٔ میز را می‌گوید و سفارش هم در اکسل ذخیره می‌شود هم همان لحظه به گوشی باریستا می‌رسد.

طراحی و کدنویسی: به‌روزرسانی: زبان: Python 3حجم کد: حدود ۲۴۰ خطمجوز: آزاد (CC0)

پاسخ کوتاه

ربات منوی کافه بله یک اسکریپت پایتون است که منو را از یک دیکشنری (دسته → آیتم → قیمت) به کیبورد دکمه‌ای تبدیل می‌کند، سبد خرید هر مشتری را در حافظه نگه می‌دارد، شمارهٔ میز را می‌پرسد، سفارش را با جمع مبلغ در orders.csv ذخیره می‌کند و همان لحظه یک اعلان به chat_id مدیر می‌فرستد. برای شخصی‌سازی فقط دیکشنری MENU عوض می‌شود.

ربات منوی کافه بله چطور کار می‌کند؟

مشتری با /start دسته‌های منو را می‌بیند (قهوه، نوشیدنی سرد، دسر)، وارد هر دسته می‌شود و با زدن دکمهٔ هر آیتم آن را به سبد اضافه می‌کند. با «ثبت سفارش» شمارهٔ میز را می‌پرسد، سفارش را با جمع مبلغ در orders.csv ذخیره می‌کند و فوراً یک پیام «سفارش جدید — میز ۴» به مدیر یا باریستا می‌فرستد. این جمع‌بندی هر چهار ربات قبلی در یک محصول واقعی است: کیبورد از ربات FAQ، ماشین حالت از ربات سفارش، اعلان به ادمین از ربات همگانی.

زبان: پایتون ۳
وابستگی: فقط requests
اجرا: long polling (بدون دامنه و SSL)
هزینه: رایگان

آموزش راه‌اندازی ربات منوی کافه و سفارش از میز بله در ۵ قدم

این مراحل را به‌ترتیب انجام بده تا ربات روی سیستم خودت یا سرور بالا بیاید. زمان تقریبی: ۲۰ دقیقه.

از BotFather در بله توکن ربات را بگیر.
پایتون را نصب کن و pip install requests را اجرا کن.
فایل bot.py را باز کن، TOKEN را بگذار و دیکشنری MENU را با منوی خودت عوض کن.
ربات را یک‌بار اجرا کن و /start بزن؛ chat_id تو در ترمینال چاپ می‌شود. آن را در ADMIN_CHAT_ID بگذار (گوشی باریستا).
دوباره python bot.py را اجرا کن، یک سفارش آزمایشی ثبت کن و orders.csv را با اکسل باز کن.

کل سورس ربات منوی کافه و سفارش از میز بله (کپی یا دانلود کن)

این همان کدی است که داخل فایل ZIP هم هست. می‌توانی مستقیم از اینجا کپی کنی. فقط یادت باشد مقدار TOKEN را با توکن رباتت عوض کنی.

bot.py
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
#  ربات منوی کافه و سفارش از میز (بله)
#  طراحی و کدنویسی: سروش مقدمی — فیلتور (filtori.com)
# ------------------------------------------------------------
#  مشتری با /start منو رو می‌بینه، با دکمه دسته و آیتم انتخاب می‌کنه،
#  سبد خریدش رو می‌بینه، شمارهٔ میز رو می‌گه و سفارش ثبت می‌شه.
#   - منو کاملاً از روی دیکشنری MENU ساخته می‌شه (فقط همون رو عوض کن)
#   - هر سفارش در orders.csv ذخیره می‌شه
#   - همان لحظه یک پیام به مدیر/بار (ADMIN_CHAT_ID) می‌ره
#
#  آموزش کامل و توضیح خط‌به‌خط:
#     https://filtori.com/bale-bot-source/cafe-menu-bot/
#  نسخهٔ حرفه‌ای با منوی QR، پنل و گزارش فروش:  https://filtori.com/cafe-menu/
#  تماس: 09122874862  |  تلگرام: https://t.me/filtori
# ------------------------------------------------------------

import csv
import time
from datetime import datetime

import requests

TOKEN = "توکن_ربات_خودت_را_اینجا_بگذار"
BASE = f"https://tapi.bale.ai/bot{TOKEN}"

# chat_id مدیر یا باریستا؛ هر سفارش جدید فوری برای این شماره ارسال می‌شه.
# (ربات رو اجرا کن، /start بزن؛ chat_id تو در ترمینال چاپ می‌شه)
ADMIN_CHAT_ID = 123456789

ORDERS_FILE = "orders.csv"

# ------------------------------------------------------------
#  منوی کافه: دسته → {نام آیتم: قیمت به تومان}
#  فقط همین بخش رو با منوی خودت عوض کن؛ دکمه‌ها خودکار ساخته می‌شن.
# ------------------------------------------------------------
MENU = {
    "☕ قهوه": {
        "اسپرسو": 65000,
        "آمریکانو": 75000,
        "لاته": 95000,
        "کاپوچینو": 95000,
    },
    "🧊 نوشیدنی سرد": {
        "آیس لاته": 105000,
        "لیموناد": 85000,
        "موهیتو": 90000,
    },
    "🍰 کیک و دسر": {
        "چیزکیک": 120000,
        "براونی": 95000,
    },
}

BTN_CART = "🧺 سبد خرید"
BTN_CHECKOUT = "✅ ثبت سفارش"
BTN_CLEAR = "🗑 خالی کردن سبد"
BTN_BACK = "↩️ بازگشت به منو"

# حافظهٔ موقت: برای هر chat_id سبد و مرحلهٔ فعلی رو نگه می‌داریم.
# (در رم است؛ با ری‌استارت پاک می‌شود — نسخهٔ واقعی دیتابیس می‌خواهد)
sessions = {}


def fmt_price(amount):
    """۹۵۰۰۰ → «۹۵٬۰۰۰ تومان» (با ارقام فارسی)"""
    fa = str.maketrans("0123456789,", "۰۱۲۳۴۵۶۷۸۹٬")
    return f"{amount:,}".translate(fa) + " تومان"


def keyboard(rows):
    """ساخت کیبورد دکمه‌ای بله از روی لیست ردیف‌ها."""
    return {
        "keyboard": [[{"text": t} for t in row] for row in rows],
        "resize_keyboard": True,
    }


def main_keyboard():
    """کیبورد صفحهٔ اول: دسته‌ها + سبد و ثبت سفارش."""
    cats = list(MENU.keys())
    rows = [cats[i:i + 2] for i in range(0, len(cats), 2)]
    rows.append([BTN_CART, BTN_CHECKOUT])
    return keyboard(rows)


def category_keyboard(cat):
    """کیبورد آیتم‌های یک دسته + دکمهٔ بازگشت."""
    items = [f"{name} — {fmt_price(p)}" for name, p in MENU[cat].items()]
    rows = [items[i:i + 2] for i in range(0, len(items), 2)]
    rows.append([BTN_CART, BTN_BACK])
    return keyboard(rows)


def send_message(chat_id, text, reply_markup=None):
    url = f"{BASE}/sendMessage"
    data = {"chat_id": chat_id, "text": text}
    if reply_markup is not None:
        data["reply_markup"] = reply_markup
    try:
        requests.post(url, json=data, timeout=15)
    except requests.RequestException as error:
        print("خطا در ارسال پیام:", error)


def get_updates(offset):
    url = f"{BASE}/getUpdates"
    params = {"offset": offset, "timeout": 30}
    try:
        return requests.get(url, params=params, timeout=40).json()
    except requests.RequestException as error:
        print("خطا در دریافت پیام‌ها:", error)
        return {"ok": False, "result": []}


def get_session(chat_id):
    if chat_id not in sessions:
        sessions[chat_id] = {"cart": {}, "step": "menu"}
    return sessions[chat_id]


def find_item(text):
    """از روی متن دکمهٔ زده‌شده، نام و قیمت آیتم رو پیدا می‌کنه."""
    name = text.split(" — ")[0].strip()
    for items in MENU.values():
        if name in items:
            return name, items[name]
    return None, None


def cart_text(cart):
    if not cart:
        return "سبد خریدت خالیه 🧺"
    lines, total = [], 0
    for name, info in cart.items():
        line_total = info["price"] * info["qty"]
        total += line_total
        lines.append(f"• {name} × {info['qty']} = {fmt_price(line_total)}")
    lines.append(f"\nجمع کل: {fmt_price(total)}")
    return "\n".join(lines)


def save_order(chat_id, table, cart):
    """ذخیرهٔ سفارش در CSV (utf-8-sig تا در اکسل فارسی درست باز شود)."""
    total = sum(i["price"] * i["qty"] for i in cart.values())
    items = " + ".join(f"{n}×{i['qty']}" for n, i in cart.items())
    is_new = False
    try:
        with open(ORDERS_FILE, "r", encoding="utf-8-sig"):
            pass
    except FileNotFoundError:
        is_new = True
    with open(ORDERS_FILE, "a", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        if is_new:
            w.writerow(["تاریخ", "میز", "اقلام", "جمع (تومان)", "chat_id"])
        w.writerow([datetime.now().strftime("%Y-%m-%d %H:%M"), table, items, total, chat_id])
    return total, items


def handle(chat_id, text):
    s = get_session(chat_id)

    if text == "/start" or text == BTN_BACK:
        s["step"] = "menu"
        send_message(chat_id, "سلام ☕ به کافهٔ ما خوش اومدی!\nیک دسته رو انتخاب کن:", main_keyboard())
        return

    # مرحلهٔ گرفتن شمارهٔ میز
    if s["step"] == "table":
        table = text.strip()
        if not table.isdigit():
            send_message(chat_id, "لطفاً فقط شمارهٔ میز رو به عدد بنویس (مثلاً 4):")
            return
        total, items = save_order(chat_id, table, s["cart"])
        send_message(
            chat_id,
            f"سفارشت ثبت شد ✅\n🪑 میز {table}\n{cart_text(s['cart'])}\n\nتا چند دقیقهٔ دیگه آماده می‌شه 🙏",
            main_keyboard(),
        )
        send_message(ADMIN_CHAT_ID, f"🔔 سفارش جدید — میز {table}\n{items}\nجمع: {fmt_price(total)}")
        s["cart"] = {}
        s["step"] = "menu"
        return

    if text == BTN_CART:
        send_message(chat_id, cart_text(s["cart"]), keyboard([[BTN_CHECKOUT, BTN_CLEAR], [BTN_BACK]]))
        return

    if text == BTN_CLEAR:
        s["cart"] = {}
        send_message(chat_id, "سبد خالی شد.", main_keyboard())
        return

    if text == BTN_CHECKOUT:
        if not s["cart"]:
            send_message(chat_id, "اول یه چیزی به سبد اضافه کن 😊", main_keyboard())
            return
        s["step"] = "table"
        send_message(chat_id, "شمارهٔ میزت رو بنویس:", {"remove_keyboard": True})
        return

    # انتخاب دسته
    if text in MENU:
        send_message(chat_id, f"{text}\nیک آیتم انتخاب کن:", category_keyboard(text))
        return

    # انتخاب آیتم
    name, price = find_item(text)
    if name:
        item = s["cart"].setdefault(name, {"price": price, "qty": 0})
        item["qty"] += 1
        send_message(chat_id, f"«{name}» اضافه شد ✅ (تعداد: {item['qty']})", main_keyboard())
        return

    send_message(chat_id, "متوجه نشدم 🤔 از دکمه‌های پایین استفاده کن.", main_keyboard())


def main():
    print("ربات منوی کافهٔ فیلتور روشن شد... (Ctrl+C برای خاموش‌کردن)")
    offset = 0
    while True:
        updates = get_updates(offset)
        if not updates.get("ok"):
            time.sleep(2)
            continue
        for update in updates.get("result", []):
            offset = update["update_id"] + 1
            message = update.get("message")
            if not message:
                continue
            chat_id = message["chat"]["id"]
            text = message.get("text", "")
            print("پیام از chat_id:", chat_id, "->", text)
            if text:
                handle(chat_id, text)
        time.sleep(0.5)


if __name__ == "__main__":
    main()
کل سورس این ربات ↑ — همین حالا کپی یا دانلود کن

کد را خط‌به‌خط بفهم

کپی‌کردن کافی نیست؛ اگر بدانی هر بخش چرا آن‌جاست، می‌توانی ربات را برای کسب‌وکار خودت تغییر بدهی. مهم‌ترین قطعه‌های ربات منوی کافه و سفارش از میز بله:

۱منو به‌شکل داده، نه کد

MENU = {
    "☕ قهوه": {"اسپرسو": 65000, "لاته": 95000},
    "🍰 کیک و دسر": {"چیزکیک": 120000},
}

همهٔ دکمه‌ها، قیمت‌ها و دسته‌ها از روی همین دیکشنری ساخته می‌شوند. تغییر منو یعنی تغییر داده، نه کدنویسی. اگر بخواهی منو را از فایل اکسل بخوانی، فقط همین یک متغیر باید پر شود.

۲ساخت کیبورد پویا

rows = [cats[i:i + 2] for i in range(0, len(cats), 2)]

دسته‌ها دوتا‌دوتا در هر ردیف چیده می‌شوند؛ هرچقدر دسته اضافه کنی کیبورد خودش بزرگ می‌شود. متن دکمهٔ آیتم‌ها «نام — قیمت» است تا مشتری قبل از انتخاب قیمت را ببیند.

۳سبد خرید در حافظه

item = s["cart"].setdefault(name, {"price": price, "qty": 0})
item["qty"] += 1

setdefault اگر آیتم در سبد نبود آن را با تعداد صفر می‌سازد، بعد یکی اضافه می‌کند. دو بار زدن «لاته» = لاته ×۲. قیمت همان لحظه در سبد ثبت می‌شود تا اگر منو وسط سفارش عوض شد، جمع مشتری تغییر نکند.

۴گرفتن شمارهٔ میز با حذف کیبورد

send_message(chat_id, "شمارهٔ میزت رو بنویس:", {"remove_keyboard": True})
...
if not table.isdigit(): return

برای این‌که مشتری به‌جای عدد روی دکمه نزند، کیبورد را برمی‌داریم. ورودی با isdigit() چک می‌شود (ارقام فارسی را هم قبول می‌کند) و تا وقتی عدد ندهد، مرحله عوض نمی‌شود.

۵اعلان لحظه‌ای به باریستا

send_message(ADMIN_CHAT_ID, f"🔔 سفارش جدید — میز {table}\n{items}\nجمع: ...")

همان لحظهٔ ثبت، یک پیام به گوشی مدیر یا تبلت بار می‌رود. این یعنی بدون هیچ پنل یا پرینتری، سفارش از میز به بار رسیده. در نسخهٔ حرفه‌ای این پیام به چاپگر آشپزخانه و پنل گزارش فروش وصل می‌شود.

۶CSV با utf-8-sig

open(ORDERS_FILE, "a", newline="", encoding="utf-8-sig")

اکسل ویندوز بدون BOM، فارسی را به‌هم‌ریخته نشان می‌دهد. utf-8-sig این علامت را اضافه می‌کند تا فایل مستقیماً و درست باز شود. ستون‌ها: تاریخ، میز، اقلام، جمع، chat_id.

۷ارقام فارسی و جداکنندهٔ هزارگان

f"{amount:,}".translate(fa) + " تومان"

قیمت‌ها با جداکنندهٔ هزارگان فارسی (٬) و ارقام فارسی نمایش داده می‌شوند: «۹۵٬۰۰۰ تومان». جزئیات کوچکی که ربات را از «دموی برنامه‌نویس» به «چیزی که مشتری کافه می‌فهمد» تبدیل می‌کند.

🔁 همین کد روی تلگرام هم اجرا می‌شود: فقط BASE را به https://api.telegram.org/bot{TOKEN} تغییر بده. جدول تفاوت API بله و تلگرام را ببین.

سوال‌های پرتکرار دربارهٔ ربات منوی کافه و سفارش از میز بله

این ربات برای رستوران و فست‌فود هم کار می‌کند؟

بله. منو از یک دیکشنری ساخته می‌شود؛ دسته‌ها و آیتم‌ها را با غذاهای خودتان عوض کنید. برای «ارسال به آدرس» به‌جای شمارهٔ میز، فقط متن سوال مرحلهٔ آخر را تغییر دهید.

مشتری چطور به ربات می‌رسد؟

لینک ربات (ble.ir/نام_ربات) را به QR تبدیل کنید و روی هر میز بچسبانید. مشتری اسکن می‌کند، /start می‌زند و منو را می‌بیند؛ بدون نصب هیچ اپ اضافه‌ای.

سفارش‌ها کجا می‌روند؟

هم در orders.csv کنار برنامه ذخیره می‌شوند (با اکسل باز می‌شود) و هم همان لحظه به chat_id مدیر (ADMIN_CHAT_ID) پیام می‌رود.

اگر چند نفر هم‌زمان سفارش بدهند قاطی می‌شود؟

خیر. سبد هر مشتری با chat_id او جدا نگه داشته می‌شود. فقط یادتان باشد سبدها در حافظهٔ موقت‌اند و با ری‌استارت ربات پاک می‌شوند.

نسخهٔ کامل با منوی QR، عکس، پرداخت و گزارش فروش می‌خواهم

این همان منوی هوشمند کافهٔ فیلتور است: منوی تصویری، سفارش از میز، پنل مدیریت و گزارش فروش. با ۰۹۱۲۲۸۷۴۸۶۲ تماس بگیرید یا صفحهٔ محصول را ببینید.

طراح و برنامه‌نویس این سورس‌ها · بنیان‌گذار فیلتور

مدیر بخش دیتابیس همراه اول و فعال در توسعهٔ نرم‌افزار از سال ۱۳۹۱؛ با سابقهٔ کار روی سامانه‌های بانکی و پرتراکنش. این سورس‌ها را طوری نوشته که ساده، خوانا و قابل توسعه باشند و مستقیم در کسب‌وکار واقعی استفاده شوند.

راه‌اندازی این ربات برایت سخت شد؟

لازم نیست خودت درگیر شوی. تیم فیلتور این ربات را نصب، شخصی‌سازی و به کسب‌وکارت وصل می‌کند.