رفتن به محتوای اصلی
📅 سطح: متوسط تا پیشرفته · رایگان

سورس ربات رزرو و نوبت‌دهی تلگرام

مشتری خدمت، روز و یکی از ساعت‌های خالی را انتخاب می‌کند و شماره‌اش را با یک دکمه می‌فرستد؛ نوبت ثبت و برای شما ارسال می‌شود و هیچ ساعتی دو بار رزرو نمی‌شود.

سوال یا راهنمایی لازم داری؟ از صفحهٔ تماس با ما بپرس.

طراحی و کدنویسی: زبان: Python 3وابستگی: requestsزمان راه‌اندازی: ۲۰ دقیقهمجوز: رایگان (CC0)به‌روزرسانی: ۲ مهر ۱۴۰۵

این ربات چه کاری انجام می‌دهد؟

این ربات برای کسب‌وکارهای نوبتی مثل سالن زیبایی، مطب، آموزشگاه و کارواش است. کاربر خدمت را انتخاب می‌کند، از بین روزهای کاری آینده (بدون روزهای تعطیل) یک روز برمی‌دارد و فقط ساعت‌های خالی همان روز را می‌بیند. بعد نام و شماره تماسش را می‌فرستد. نوبت در bookings.csv ذخیره و برای مدیر ارسال می‌شود.

⚠️ نکتهٔ مهم: ربات تلگرام باید روی سروری اجرا شود که به api.telegram.org دسترسی دارد؛ روی خیلی از هاست‌های داخل ایران این آدرس مستقیم باز نیست. اگر ربات اجرا نشد، معمولاً مشکل از دسترسی سرور است نه از کد. اگر گیر کردی، از طریق صفحهٔ تماس با ما کمک بگیر.

آموزش راه‌اندازی قدم‌به‌قدم

این مراحل را به‌ترتیب انجام بده تا ربات روی سیستم خودت یا سرور بالا بیاید. داخل فایل ZIP یک راهنمای README هم هست.

از BotFather در تلگرام توکن ربات را بگیر.
پایتون را نصب کن و pip install requests را اجرا کن.
در فایل bot.py مقدار TOKEN و ADMIN_CHAT_ID را بگذار.
لیست SERVICES، ساعت‌های TIME_SLOTS و روزهای تعطیل CLOSED_WEEKDAYS را مطابق کسب‌وکار خودت تنظیم کن.
python bot.py را اجرا کن، /start بزن و یک نوبت آزمایشی ثبت کن؛ نوبت در bookings.csv ذخیره می‌شود.

کل سورس ربات (کپی یا دانلود کن)

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

bot.py
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
#  ربات رزرو و نوبت‌دهی تلگرام
#  طراحی و کدنویسی: سروش مقدمی — فیلتور (filtori.com)
# ------------------------------------------------------------
#  مناسب سالن زیبایی، مطب، آموزشگاه، کارواش و هر کسب‌وکار نوبتی:
#   ۱) کاربر خدمت رو انتخاب می‌کنه
#   ۲) از بین روزهای کاری آینده یک روز انتخاب می‌کنه
#   ۳) فقط ساعت‌های «خالی» همون روز بهش نشون داده می‌شه
#   ۴) نام و شمارهٔ تماس (با یک دکمه) رو می‌فرسته
#  نوبت در bookings.csv ذخیره می‌شه و برای مدیر هم ارسال می‌شه.
#  یک ساعت هیچ‌وقت دو بار رزرو نمی‌شه.
#
#  ⚠️ نکته: ربات تلگرام باید روی سروری اجرا بشه که به
#     api.telegram.org دسترسی داره.
#
#  آموزش کامل:
#     https://filtori.com/telegram-bot-source/booking-bot/
#  سوال یا راهنمایی:  https://filtori.com/contact/
#  نسخهٔ اختصاصی با تقویم شمسی، پیامک یادآوری و پنل؟  09122874862
# ------------------------------------------------------------

import csv
import os
import time
from datetime import date, datetime, timedelta

import requests

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

ADMIN_CHAT_ID = 123456789  # <-- chat_id مدیر برای دریافت نوبت‌های جدید

# ------------------------- تنظیمات کسب‌وکار -------------------------
SERVICES = ["کوتاهی مو", "رنگ مو", "مانیکور", "مشاوره"]
TIME_SLOTS = ["10:00", "11:00", "12:00", "16:00", "17:00", "18:00"]
DAYS_AHEAD = 7            # چند روز آینده برای رزرو نمایش داده بشه
CLOSED_WEEKDAYS = {4}     # روزهای تعطیل؛ 4 = جمعه (دوشنبه=0 ... یکشنبه=6)
BOOKINGS_FILE = "bookings.csv"
# -----------------------------------------------------------------

WEEKDAY_NAMES = ["دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "یکشنبه"]
CANCEL = "❌ انصراف"

sessions = {}  # حافظهٔ موقت گفتگو برای هر کاربر


def send_message(chat_id, text, keyboard=None):
    """ارسال پیام؛ keyboard یک لیست از ردیف‌های دکمه است یا None برای حذف کیبورد."""
    data = {"chat_id": chat_id, "text": text}
    if keyboard:
        data["reply_markup"] = {"keyboard": keyboard, "resize_keyboard": True}
    else:
        data["reply_markup"] = {"remove_keyboard": True}
    try:
        requests.post(f"{BASE}/sendMessage", json=data, timeout=15)
    except requests.RequestException as error:
        print("خطا در ارسال پیام:", error)


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


def rows(items, size=2):
    """تبدیل یک لیست ساده به ردیف‌های دکمه (هر ردیف size دکمه)."""
    buttons = [{"text": item} for item in items]
    return [buttons[i:i + size] for i in range(0, len(buttons), size)] + [[{"text": CANCEL}]]


def open_days():
    """روزهای کاری آینده؛ خروجی: {برچسب دکمه: تاریخ}"""
    days = {}
    today = date.today()
    for offset in range(1, DAYS_AHEAD + 1):
        day = today + timedelta(days=offset)
        if day.weekday() in CLOSED_WEEKDAYS:
            continue
        label = f"{WEEKDAY_NAMES[day.weekday()]} {day.isoformat()}"
        days[label] = day.isoformat()
    return days


def load_bookings():
    if not os.path.exists(BOOKINGS_FILE):
        return []
    with open(BOOKINGS_FILE, "r", newline="", encoding="utf-8-sig") as file:
        return list(csv.DictReader(file))


def free_slots(day_iso):
    """ساعت‌هایی از یک روز که هنوز رزرو نشده‌اند."""
    taken = {b["time"] for b in load_bookings() if b["date"] == day_iso}
    return [slot for slot in TIME_SLOTS if slot not in taken]


def save_booking(data):
    is_new = not os.path.exists(BOOKINGS_FILE)
    with open(BOOKINGS_FILE, "a", newline="", encoding="utf-8-sig") as file:
        writer = csv.DictWriter(
            file, fieldnames=["created", "date", "time", "service", "name", "phone", "chat_id"]
        )
        if is_new:
            writer.writeheader()
        writer.writerow(data)


def start(chat_id):
    sessions[chat_id] = {"step": "service"}
    send_message(chat_id, "سلام 👋 برای رزرو نوبت، اول خدمت موردنظرت رو انتخاب کن:", rows(SERVICES))


def handle(message):
    chat_id = message["chat"]["id"]
    text = message.get("text", "")
    contact = message.get("contact")

    if text == "/start":
        start(chat_id)
        return
    if text == CANCEL:
        sessions.pop(chat_id, None)
        send_message(chat_id, "رزرو لغو شد. هر وقت خواستی دوباره /start بزن.")
        return
    if chat_id not in sessions:
        send_message(chat_id, "برای رزرو نوبت، دستور /start را بفرستید.")
        return

    session = sessions[chat_id]
    step = session["step"]

    if step == "service":
        if text not in SERVICES:
            send_message(chat_id, "لطفاً یکی از خدمات رو از دکمه‌ها انتخاب کن.", rows(SERVICES))
            return
        session["service"] = text
        session["days"] = open_days()
        session["step"] = "day"
        send_message(chat_id, "کدوم روز برات مناسبه؟", rows(list(session["days"])))

    elif step == "day":
        if text not in session["days"]:
            send_message(chat_id, "لطفاً روز رو از دکمه‌ها انتخاب کن.", rows(list(session["days"])))
            return
        day_iso = session["days"][text]
        slots = free_slots(day_iso)
        if not slots:
            send_message(chat_id, "همهٔ ساعت‌های این روز پر شده 😕 یک روز دیگه انتخاب کن.",
                         rows(list(session["days"])))
            return
        session["date"] = day_iso
        session["day_label"] = text
        session["step"] = "time"
        send_message(chat_id, "ساعت‌های خالی این روز:", rows(slots, size=3))

    elif step == "time":
        if text not in free_slots(session["date"]):
            send_message(chat_id, "این ساعت در دسترس نیست؛ یکی از دکمه‌ها رو انتخاب کن.",
                         rows(free_slots(session["date"]), size=3))
            return
        session["time"] = text
        session["step"] = "name"
        send_message(chat_id, "نام و نام خانوادگی‌ات رو بنویس:", [[{"text": CANCEL}]])

    elif step == "name":
        if not text:
            return
        session["name"] = text
        session["step"] = "phone"
        send_message(
            chat_id,
            "در آخر شمارهٔ تماست رو با دکمهٔ زیر بفرست (یا تایپ کن):",
            [[{"text": "📱 ارسال شماره تماس", "request_contact": True}], [{"text": CANCEL}]],
        )

    elif step == "phone":
        phone = contact["phone_number"] if contact else text
        if not phone:
            return
        # بررسی دوباره: شاید در این فاصله کس دیگری همین ساعت را گرفته باشد
        if session["time"] not in free_slots(session["date"]):
            session["step"] = "time"
            send_message(chat_id, "متأسفانه این ساعت همین الان رزرو شد 😕 ساعت دیگری انتخاب کن:",
                         rows(free_slots(session["date"]), size=3))
            return

        booking = {
            "created": datetime.now().strftime("%Y-%m-%d %H:%M"),
            "date": session["date"],
            "time": session["time"],
            "service": session["service"],
            "name": session["name"],
            "phone": phone,
            "chat_id": chat_id,
        }
        save_booking(booking)
        summary = (
            f"🗓 {session['day_label']} ساعت {session['time']}\n"
            f"✂️ خدمت: {session['service']}\n"
            f"👤 {session['name']}  |  📞 {phone}"
        )
        send_message(chat_id, "نوبتت ثبت شد ✅\n\n" + summary + "\n\nمنتظرت هستیم 🌷")
        send_message(ADMIN_CHAT_ID, "📌 نوبت جدید:\n\n" + summary)
        del sessions[chat_id]


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 message:
                handle(message)

        time.sleep(0.5)


if __name__ == "__main__":
    main()
کل سورس این ربات ↑

⬇ دانلود فایل ZIP این ربات

شخصی‌سازی سریع

با چند تغییر کوچک، ربات را مخصوص کسب‌وکار خودت کن.

خدمات و ساعت‌ها

در بخش تنظیمات، SERVICES و TIME_SLOTS را با خدمات و ساعت‌های کاری خودت عوض کن.

روزهای تعطیل و بازهٔ رزرو

CLOSED_WEEKDAYS روزهای تعطیل است (دوشنبه=0 … جمعه=4 … یکشنبه=6) و DAYS_AHEAD تعداد روزهای قابل رزرو.

تاریخ شمسی

این سورس تاریخ میلادی نشان می‌دهد تا بدون کتابخانهٔ اضافه اجرا شود. برای تاریخ شمسی می‌توان کتابخانهٔ jdatetime را اضافه کرد؛ در نسخهٔ اختصاصی یادآوری پیامکی و پنل هم اضافه می‌شود.

خطاهای رایج و راه‌حل

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

پیام خطاعلتراه‌حل
401 Unauthorizedتوکن اشتباه است یا فاصلهٔ اضافه دارد.توکن را دوباره از BotFather کپی کن و بین دو علامت نقل‌قول بگذار.
409 Conflictیک نسخهٔ دیگر از ربات در حال اجراست یا روی ربات وب‌هوک تنظیم شده.نسخهٔ دیگر را خاموش کن. اگر قبلاً وب‌هوک گذاشته‌ای، آدرس api.telegram.org/bot<TOKEN>/deleteWebhook را یک بار باز کن.
Timeout یا ConnectionErrorسرور به api.telegram.org دسترسی ندارد.ربات را روی سروری اجرا کن که به تلگرام دسترسی دارد؛ روی بیشتر هاست‌های داخل ایران این آدرس باز نیست.
ModuleNotFoundError: requestsکتابخانهٔ requests نصب نشده.دستور pip install -r requirements.txt یا pip install requests را اجرا کن.
429 Too Many Requestsدر زمان کوتاه پیام زیادی ارسال شده.بین ارسال‌ها مکث بگذار؛ تلگرام حدود ۳۰ پیام در ثانیه اجازه می‌دهد.
ربات در گروه جواب نمی‌دهدPrivacy Mode ربات روشن است.در BotFather دستور /setprivacy را بزن و آن را Disable کن، یا ربات را ادمین گروه کن.

اجرای ۲۴ ساعته روی سرور لینوکس

با python bot.py ربات تا وقتی ترمینال باز است کار می‌کند. برای اجرای دائمی روی سرور لینوکس (Ubuntu/Debian)، فایل‌ها را در پوشهٔ /opt/mybot بگذار و یک سرویس systemd بساز تا ربات بعد از خطا یا ری‌استارت سرور خودش دوباره روشن شود.

/etc/systemd/system/mybot.service
[Unit]
Description=Filtori Telegram Bot
After=network-online.target

[Service]
WorkingDirectory=/opt/mybot
ExecStart=/usr/bin/python3 /opt/mybot/bot.py
Environment=PYTHONUNBUFFERED=1
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
فایل سرویس
terminal
sudo nano /etc/systemd/system/mybot.service   # متن بالا را اینجا بگذار
sudo systemctl daemon-reload
sudo systemctl enable --now mybot             # روشن‌کردن + اجرای خودکار بعد از ری‌استارت
journalctl -u mybot -f                        # دیدن لاگ‌ها
دستورها

راه‌اندازی روی سرور برایت سخت است؟ نصب و راه‌اندازی را به ما بسپار.

درخواست راه‌اندازی

از سورس رایگان تا ربات حرفه‌ای کسب‌وکار

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

امکانسورس رایگاننسخهٔ اختصاصی
ذخیره‌سازیفایل CSV/TXTدیتابیس
پنل مدیریت—✓
پرداخت آنلاین—✓
اتصال به سایت / CRM—✓
نصب و پشتیبانیخودتتیم فیلتور

سوال‌های پرتکرار دربارهٔ این ربات

ممکن است دو نفر یک ساعت را رزرو کنند؟

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

نوبت‌ها کجا ذخیره می‌شوند؟

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

می‌شود برای مشتری یادآوری فرستاد؟

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

سوال یا مشکلی داشتم از کجا راهنمایی بگیرم؟

از صفحهٔ تماس با ما در filtori.com/contact پیام بدهید یا با شمارهٔ ۰۹۱۲۲۸۷۴۸۶۲ تماس بگیرید؛ اگر نسخهٔ اختصاصی همین ربات را برای کسب‌وکارتان بخواهید هم همین مسیر را استفاده کنید.

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

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

سوالی داری یا راهنمایی می‌خواهی؟

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

سورس‌های دیگر ربات تلگرام