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

سورس ربات پشتیبانی و تیکت بله

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

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

پاسخ کوتاه

ربات پشتیبانی و تیکت بله یک اسکریپت پایتون است که هر پیام کاربر را با شمارهٔ یکتا در فایل tickets.csv ثبت می‌کند و برای ADMIN_CHAT_ID می‌فرستد. مدیر با دستور «/reply شماره متن» جواب می‌دهد، ربات جواب را به chat_id صاحب تیکت می‌رساند و وضعیت تیکت را «پاسخ داده شد» می‌کند.

ربات پشتیبانی و تیکت بله چه کاری انجام می‌دهد؟

وقتی پیام‌های پشتیبانی در پیوی ادمین‌ها پخش می‌شود، پیگیری گم می‌شود و مشتری مجبور است چند بار پیام بدهد. این ربات هر پیام را به یک تیکت با شمارهٔ پیگیری تبدیل می‌کند و در یک فایل نگه می‌دارد. مدیر (یا یک گروه از پشتیبان‌ها) تیکت را در بله می‌بیند و با /reply جواب می‌دهد؛ مشتری جواب را از خود ربات می‌گیرد. با /open هم فهرست تیکت‌های بی‌پاسخ نمایش داده می‌شود.

زبان: پایتون ۳
وابستگی: فقط requests
ذخیره: tickets.csv
دستورهای مدیر: /reply و /open
هزینه: رایگان

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

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

از BotFather در بله یک ربات بساز و توکن آن را بگیر.
پایتون ۳ را نصب کن و pip install requests را اجرا کن.
در فایل bot.py مقدار TOKEN را بگذار.
ربات را اجرا کن و /start بزن؛ chat_id تو در ترمینال چاپ می‌شود. آن را در ADMIN_CHAT_ID بگذار و ربات را دوباره اجرا کن.
از یک حساب دیگر پیام بفرست، تیکت را در بلهٔ خودت ببین و با /reply شماره متن جواب بده.

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

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

bot.py
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
#  ربات پشتیبانی و تیکت بله
#  طراحی و کدنویسی: سروش مقدمی — فیلتور (filtori.com)
# ------------------------------------------------------------
#  هر پیامی که کاربر بفرسته، یک «تیکت» با شمارهٔ پیگیری می‌شه:
#   - تیکت داخل فایل tickets.csv ذخیره می‌شه (با اکسل باز می‌شه)
#   - همون لحظه برای مدیر (ادمین) فرستاده می‌شه
#   - مدیر با دستور  /reply شماره_تیکت متن_پاسخ  جواب می‌ده
#     و جواب مستقیم برای همون کاربر ارسال می‌شه
#   - مدیر با دستور  /open  تیکت‌های بی‌پاسخ رو می‌بینه
#
#  آموزش کامل:
#     https://filtori.com/bale-bot-source/ticket-bot/
#  سوال یا راهنمایی:  https://filtori.com/contact/
#  نسخهٔ اختصاصی با پنل و چند اپراتور؟  09122874862
# ------------------------------------------------------------

import csv
import os
import time
from datetime import datetime

import requests

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

# chat_id عددی مدیر؛ ربات رو اجرا کن، /start بزن تا در ترمینال چاپ بشه.
ADMIN_CHAT_ID = 123456789  # <-- این عدد رو با chat_id خودت عوض کن

TICKETS_FILE = "tickets.csv"
FIELDS = ["id", "date", "chat_id", "name", "message", "status"]


def send_message(chat_id, text):
    url = f"{BASE}/sendMessage"
    try:
        requests.post(url, json={"chat_id": chat_id, "text": text}, 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, ValueError) as error:
        print("خطا در دریافت پیام‌ها:", error)
        return {"ok": False, "result": []}


def load_tickets():
    """خواندن همهٔ تیکت‌ها از فایل CSV."""
    if not os.path.exists(TICKETS_FILE):
        return []
    with open(TICKETS_FILE, "r", newline="", encoding="utf-8-sig") as file:
        return list(csv.DictReader(file))


def save_tickets(tickets):
    """بازنویسی کامل فایل تیکت‌ها (برای تغییر وضعیت)."""
    with open(TICKETS_FILE, "w", newline="", encoding="utf-8-sig") as file:
        writer = csv.DictWriter(file, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(tickets)


def create_ticket(chat_id, name, message):
    """ساخت تیکت جدید و برگرداندن شمارهٔ آن."""
    tickets = load_tickets()
    ticket_id = max([int(t["id"]) for t in tickets], default=0) + 1
    tickets.append(
        {
            "id": str(ticket_id),
            "date": datetime.now().strftime("%Y-%m-%d %H:%M"),
            "chat_id": str(chat_id),
            "name": name,
            "message": message,
            "status": "باز",
        }
    )
    save_tickets(tickets)
    return ticket_id


def admin_reply(text):
    """اجرای دستور /reply شماره متن  توسط مدیر."""
    parts = text.split(maxsplit=2)
    if len(parts) < 3 or not parts[1].isdigit():
        send_message(ADMIN_CHAT_ID, "فرمت درست:\n/reply 12 متن پاسخ شما")
        return

    ticket_id, answer = parts[1], parts[2]
    tickets = load_tickets()
    for ticket in tickets:
        if ticket["id"] == ticket_id:
            send_message(
                int(ticket["chat_id"]),
                f"📩 پاسخ پشتیبانی به تیکت #{ticket_id}:\n\n{answer}",
            )
            ticket["status"] = "پاسخ داده شد"
            save_tickets(tickets)
            send_message(ADMIN_CHAT_ID, f"پاسخ تیکت #{ticket_id} ارسال شد ✅")
            return

    send_message(ADMIN_CHAT_ID, f"تیکتی با شمارهٔ {ticket_id} پیدا نشد.")


def admin_open_list():
    """نمایش تیکت‌های بی‌پاسخ برای مدیر."""
    open_tickets = [t for t in load_tickets() if t["status"] == "باز"]
    if not open_tickets:
        send_message(ADMIN_CHAT_ID, "هیچ تیکت بی‌پاسخی نداری 👌")
        return
    lines = [f"#{t['id']} | {t['name']} | {t['message'][:40]}" for t in open_tickets[-15:]]
    send_message(ADMIN_CHAT_ID, "تیکت‌های باز:\n\n" + "\n".join(lines))


def handle(message):
    chat_id = message["chat"]["id"]
    text = message.get("text", "")
    user = message.get("from", {})
    name = (user.get("first_name", "") + " " + user.get("last_name", "")).strip() or "کاربر"

    # --- دستورهای مدیر ---
    if chat_id == ADMIN_CHAT_ID:
        if text.startswith("/reply"):
            admin_reply(text)
        elif text == "/open":
            admin_open_list()
        else:
            send_message(
                chat_id,
                "دستورهای مدیر:\n/open  ← تیکت‌های بی‌پاسخ\n/reply 12 متن  ← پاسخ به تیکت",
            )
        return

    # --- کاربران ---
    if text == "/start":
        send_message(
            chat_id,
            f"سلام {name} 👋\n"
            "به پشتیبانی ما خوش اومدی. سوال یا مشکلت رو همین‌جا بنویس؛\n"
            "یک شمارهٔ پیگیری می‌گیری و کارشناس ما همین‌جا جواب می‌ده.",
        )
        return

    if not text:
        send_message(chat_id, "فعلاً فقط پیام متنی پشتیبانی می‌شه؛ لطفاً مشکلت رو بنویس.")
        return

    ticket_id = create_ticket(chat_id, name, text)
    send_message(
        chat_id,
        f"پیامت ثبت شد ✅\nشمارهٔ پیگیری: #{ticket_id}\n"
        "به‌زودی جوابت همین‌جا ارسال می‌شه.",
    )
    send_message(
        ADMIN_CHAT_ID,
        f"🎫 تیکت جدید #{ticket_id}\n👤 {name} (chat_id: {chat_id})\n\n{text}\n\n"
        f"برای پاسخ:\n/reply {ticket_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 not message:
                continue
            print("پیام از chat_id:", message["chat"]["id"])
            handle(message)

        time.sleep(0.5)


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

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

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

۱شمارهٔ تیکت یکتا

ticket_id = max([int(t["id"]) for t in tickets], default=0) + 1

شمارهٔ تیکت بعدی از بزرگ‌ترین شمارهٔ موجود ساخته می‌شود، نه از تعداد سطرها؛ برای همین اگر روزی سطری را از فایل پاک کنی، شماره‌ها تکراری نمی‌شوند. default=0 هم باعث می‌شود اولین تیکت شمارهٔ ۱ بگیرد.

۲فایل CSV که اکسل فارسی را درست نشان بدهد

open(TICKETS_FILE, "w", newline="", encoding="utf-8-sig")

utf-8-sig یک BOM به ابتدای فایل اضافه می‌کند تا اکسل متن فارسی را به‌هم‌ریخته نشان ندهد. newline="" هم جلوی سطرهای خالی اضافه در ویندوز را می‌گیرد.

۳جدا کردن پیام مدیر از پیام مشتری

if chat_id == ADMIN_CHAT_ID:
    if text.startswith("/reply"):
        admin_reply(text)

هر پیامی که از chat_id مدیر بیاید دستور حساب می‌شود و تیکت ساخته نمی‌شود. این شرط همچنین امنیت ربات است: هیچ کاربر عادی نمی‌تواند با /reply به جای شما جواب بدهد.

۴خواندن دستور /reply

parts = text.split(maxsplit=2)
ticket_id, answer = parts[1], parts[2]

maxsplit=2 متن را فقط به سه تکه می‌شکند: دستور، شمارهٔ تیکت و بقیهٔ متن. یعنی پاسخ مدیر می‌تواند فاصله و چند جمله داشته باشد و خراب نشود.

۵اطلاع هم‌زمان به مشتری و مدیر

send_message(chat_id, f"پیامت ثبت شد ✅\nشمارهٔ پیگیری: #{ticket_id}...")
send_message(ADMIN_CHAT_ID, f"🎫 تیکت جدید #{ticket_id}...")

مشتری فوراً شمارهٔ پیگیری می‌گیرد و مطمئن می‌شود پیامش رسیده؛ مدیر هم متن تیکت را همراه دستور آمادهٔ پاسخ می‌بیند و فقط کافی است متنش را کامل کند.

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

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

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

مدیر در چت خودش با ربات، دستور /reply بعد شمارهٔ تیکت و بعد متن پاسخ را می‌فرستد؛ مثلاً /reply 12 سفارش شما فردا ارسال می‌شود. ربات پاسخ را مستقیم برای همان مشتری می‌فرستد.

چند پشتیبان می‌توانند هم‌زمان جواب بدهند؟

بله. یک گروه در بله بساز، ربات را عضو کن و chat_id گروه را در ADMIN_CHAT_ID بگذار؛ همهٔ اعضای گروه تیکت‌ها را می‌بینند و می‌توانند با /reply جواب بدهند.

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

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

عکس و فایل هم تیکت می‌شود؟

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

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

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

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

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

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

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