├── README.md └── invoice-bot.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Invoice Generator Bot with Telegram Integration 3 | 4 | This project is a Telegram bot that allows users to generate sales invoices by interacting with the bot. It collects user details, such as their phone number and store information, then allows them to add items to an invoice. Once the items are added, the bot generates a PDF invoice and sends it to the user. 5 | 6 | ### Features 7 | - **User Data Management**: Stores user data such as phone number, store name, and seller name in a JSON file. 8 | - **Invoice Generation**: Allows users to add items and generate a sales invoice in PDF format. 9 | - **Telegram Integration**: Full integration with Telegram to interact with users and receive their inputs. 10 | - **Arabic Support**: The invoice is generated with Arabic text and right-to-left support, using the `arabic_reshaper` and `bidi` libraries. 11 | 12 | ### Requirements 13 | - Python 3.x 14 | - `fpdf` for generating PDF invoices. 15 | - `telegram` and `telegram.ext` for Telegram bot functionality. 16 | - `arabic_reshaper` and `bidi` for Arabic text support. 17 | 18 | ### Installation 19 | 1. Clone the repository: 20 | ```bash 21 | git clone https://github.com/your-username/telegram-invoice-bot.git 22 | cd telegram-invoice-bot 23 | ``` 24 | 25 | 2. Install the required dependencies: 26 | ```bash 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | 3. Set up your Telegram bot: 31 | - Create a new bot via [BotFather](https://core.telegram.org/bots#botfather) and get your bot token. 32 | - Replace the placeholder token in the script with your actual bot token: 33 | ```python 34 | application = Application.builder().token("YOUR-BOT-TOKEN-HERE").build() 35 | ``` 36 | 37 | 4. Run the bot: 38 | ```bash 39 | python bot.py 40 | ``` 41 | 42 | ### Usage 43 | 1. Start the bot by sending `/start` to it. 44 | 2. Share your phone number and input your store information. 45 | 3. Add items to the invoice by entering product name, quantity, and price. 46 | 4. Request the invoice by clicking on the "Generate Invoice" option, and the bot will send you a PDF invoice. 47 | 48 | ### Notes 49 | - The bot stores user data in a JSON file (`user_data.json`), which includes their phone number, store name, and invoice items. 50 | - Ensure that the required fonts (`Vazir.ttf` and `Vazir-Bold.ttf`) are available in your project directory for Arabic text support. 51 | 52 | -------------------------------------------------------------------------------- /invoice-bot.py: -------------------------------------------------------------------------------- 1 | from fpdf import FPDF 2 | import os 3 | import json 4 | from telegram import ReplyKeyboardMarkup, KeyboardButton, Contact 5 | from telegram.ext import Application, CommandHandler, MessageHandler, filters 6 | import logging 7 | import arabic_reshaper 8 | from bidi.algorithm import get_display 9 | 10 | # تنظیمات اولیه 11 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 12 | level=logging.INFO) 13 | logger = logging.getLogger(__name__) 14 | 15 | # مسیر فایل JSON برای ذخیره‌سازی اطلاعات 16 | USER_DATA_FILE = 'user_data.json' 17 | 18 | # تابع ذخیره‌سازی اطلاعات کاربر 19 | def save_user_data(user_id, data): 20 | if os.path.exists(USER_DATA_FILE): 21 | with open(USER_DATA_FILE, 'r', encoding='utf-8') as file: 22 | user_data = json.load(file) 23 | else: 24 | user_data = {} 25 | 26 | user_data[str(user_id)] = data 27 | with open(USER_DATA_FILE, 'w', encoding='utf-8') as file: 28 | json.dump(user_data, file, ensure_ascii=False, indent=4) 29 | 30 | # تابع دریافت اطلاعات کاربر 31 | def get_user_data(user_id): 32 | if os.path.exists(USER_DATA_FILE): 33 | with open(USER_DATA_FILE, 'r', encoding='utf-8') as file: 34 | user_data = json.load(file) 35 | return user_data.get(str(user_id), {}) 36 | return {} 37 | 38 | # کلاس ایجاد فاکتور 39 | class InvoicePDF(FPDF): 40 | def header(self): 41 | if os.path.exists('Vazir.ttf'): 42 | self.add_font('Vazir', 'B', 'Vazir-Bold.ttf', uni=True) 43 | self.add_font('Vazir', '', 'Vazir.ttf', uni=True) 44 | self.set_font('Vazir', '', 14) 45 | else: 46 | self.set_font('Arial', '', 14) 47 | logger.warning("فایل فونت یافت نشد، از Arial استفاده می‌شود.") 48 | # عنوان فاکتور 49 | header_text = get_display(arabic_reshaper.reshape('فاکتور فروش')) 50 | self.cell(0, 10, header_text, 0, 1, 'C') 51 | self.ln(10) 52 | 53 | # اطلاعات فروشنده و خریدار 54 | user_data = get_user_data(self.user_id) 55 | store_name = user_data.get('store_name', 'نام فروشگاه تعریف نشده') 56 | seller_name = user_data.get('seller_name', 'نام فروشنده تعریف نشده') 57 | self.set_font('Vazir' if os.path.exists('Vazir.ttf') else 'Arial', '', 12) 58 | self.cell(95, 10, get_display(arabic_reshaper.reshape(f'تاریخ: 1402/03/13')), 1, 0, 'R') 59 | self.cell(95, 10, get_display(arabic_reshaper.reshape('شماره فاکتور: 0238')), 1, 1, 'R') 60 | self.cell(95, 10, get_display(arabic_reshaper.reshape(f'توسط: {seller_name}')), 1, 0, 'R') 61 | self.cell(95, 10, get_display(arabic_reshaper.reshape(f'فروشگاه: {store_name}')), 1, 1, 'R') 62 | self.ln(10) 63 | 64 | def footer(self): 65 | self.set_y(-30) 66 | self.set_font('Vazir' if os.path.exists('Vazir.ttf') else 'Arial', '', 12) 67 | footer_text = get_display(arabic_reshaper.reshape('مبلغ به حروف: سه میلیون و چهارصد و چهل و دو هزار ریال')) 68 | self.cell(0, 10, footer_text, 0, 1, 'C') 69 | self.ln(5) 70 | self.cell(95, 10, get_display(arabic_reshaper.reshape('امضاء فروشنده')), 0, 0, 'L') 71 | self.cell(95, 10, get_display(arabic_reshaper.reshape('امضاء خریدار')), 0, 1, 'R') 72 | 73 | def invoice_body(self, items): 74 | # Table Header 75 | self.set_font('Vazir' if os.path.exists('Vazir.ttf') else 'Arial', '', 12) 76 | self.cell(40, 10, get_display(arabic_reshaper.reshape('قیمت کل (ریال)')), 1, 0, 'C') 77 | self.cell(40, 10, get_display(arabic_reshaper.reshape('قیمت واحد (ریال)')), 1, 0, 'C') 78 | self.cell(20, 10, get_display(arabic_reshaper.reshape('تعداد')), 1, 0, 'C') 79 | self.cell(60, 10, get_display(arabic_reshaper.reshape('شرح کالا یا خدمات')), 1, 0, 'C') 80 | self.cell(10, 10, get_display(arabic_reshaper.reshape('ردیف')), 1, 1, 'C') 81 | 82 | # Table Body 83 | self.set_font('Vazir' if os.path.exists('Vazir.ttf') else 'Arial', '', 12) 84 | total_price = 0 85 | for idx, item in enumerate(items, start=1): 86 | name, quantity, unit_price = item 87 | total = quantity * unit_price 88 | total_price += total 89 | idx_text = str(idx) 90 | name_text = get_display(arabic_reshaper.reshape(name)) 91 | quantity_text = str(quantity) 92 | unit_price_text = get_display(arabic_reshaper.reshape(f'{unit_price:,}')) 93 | total_text = get_display(arabic_reshaper.reshape(f'{total:,}')) 94 | self.cell(40, 10, total_text, 1, 0, 'C') 95 | self.cell(40, 10, unit_price_text, 1, 0, 'C') 96 | self.cell(20, 10, quantity_text, 1, 0, 'C') 97 | self.cell(60, 10, name_text, 1, 0, 'C') 98 | self.cell(10, 10, idx_text, 1, 1, 'C') 99 | 100 | # Total Price 101 | total_price_text = get_display(arabic_reshaper.reshape(f'{total_price:,} ریال')) 102 | self.cell(40, 10, total_price_text, 1, 0, 'C') 103 | self.cell(130, 10, get_display(arabic_reshaper.reshape('جمع کل')), 1, 1, 'R') 104 | 105 | # تابع ایجاد فاکتور 106 | def generate_invoice_pdf(file_path, items, user_id): 107 | pdf = InvoicePDF() 108 | pdf.user_id = user_id 109 | pdf.add_page() 110 | pdf.invoice_body(items) 111 | pdf.output(file_path) 112 | 113 | # تابع مدیریت بات تلگرام 114 | async def start(update, context): 115 | user_id = update.effective_user.id 116 | user_data = get_user_data(user_id) 117 | if 'phone_number' in user_data: 118 | # اگر شماره تلفن قبلا ذخیره شده بود، مستقیما منوی افزودن آیتم و صدور فاکتور را نمایش دهد 119 | keyboard = [[KeyboardButton("افزودن آیتم"), KeyboardButton("صدور فاکتور")]] 120 | reply_markup = ReplyKeyboardMarkup(keyboard) 121 | await update.message.reply_text('سلام! لطفاً انتخاب کنید:', reply_markup=reply_markup) 122 | else: 123 | # اگر شماره تلفن ذخیره نشده بود، درخواست اشتراک‌گذاری شماره تلفن را ارسال کند 124 | contact_keyboard = KeyboardButton(text="اشتراک‌گذاری شماره تلفن", request_contact=True) 125 | reply_markup = ReplyKeyboardMarkup([[contact_keyboard]], one_time_keyboard=True) 126 | await update.message.reply_text('لطفاً شماره تلفن خود را به اشتراک بگذارید:', reply_markup=reply_markup) 127 | 128 | async def contact_handler(update, context): 129 | contact: Contact = update.effective_message.contact 130 | user_id = update.effective_user.id 131 | phone_number = contact.phone_number 132 | user_data = get_user_data(user_id) 133 | user_data['phone_number'] = phone_number 134 | user_data['state'] = 'awaiting_store_info' 135 | save_user_data(user_id, user_data) 136 | await update.message.reply_text('شماره تلفن شما ذخیره شد. لطفاً نام فروشگاه و نام فروشنده را به شکل زیر وارد کنید:\n\nفروشگاه: نام فروشگاه - فروشنده: نام فروشنده') 137 | 138 | async def handle_store_info(update, context): 139 | user_id = update.effective_user.id 140 | user_data = get_user_data(user_id) 141 | if user_data.get('state') == 'awaiting_store_info': 142 | try: 143 | store_info = update.message.text.split('-') 144 | store_name = store_info[0].split(':')[1].strip() 145 | seller_name = store_info[1].split(':')[1].strip() 146 | user_data['store_name'] = store_name 147 | user_data['seller_name'] = seller_name 148 | user_data['state'] = 'ready' 149 | save_user_data(user_id, user_data) 150 | await update.message.reply_text('اطلاعات فروشگاه شما ذخیره شد.') 151 | # نمایش منوی افزودن آیتم و صدور فاکتور پس از ذخیره اطلاعات فروشگاه 152 | keyboard = [[KeyboardButton("افزودن آیتم"), KeyboardButton("صدور فاکتور")]] 153 | reply_markup = ReplyKeyboardMarkup(keyboard) 154 | await update.message.reply_text('لطفاً انتخاب کنید:', reply_markup=reply_markup) 155 | except (IndexError, ValueError): 156 | await update.message.reply_text('فرمت وارد شده صحیح نیست. لطفاً به شکل صحیح وارد کنید:\n\nفروشگاه: نام فروشگاه - فروشنده: نام فروشنده') 157 | else: 158 | await handle_add_item(update, context) 159 | 160 | async def handle_add_item(update, context): 161 | if update.message.text == "صدور فاکتور": 162 | await generate_invoice(update, context) 163 | return 164 | try: 165 | item_text = update.message.text.split('-') 166 | name_quantity = item_text[0].strip() 167 | name, quantity = name_quantity.split(':') 168 | name = name.strip() 169 | quantity = int(quantity.strip()) 170 | unit_price = int(item_text[1].strip()) 171 | if 'items' not in context.user_data: 172 | context.user_data['items'] = [] 173 | context.user_data['items'].append((name, quantity, unit_price)) 174 | await update.message.reply_text(f'محصول {name} با تعداد {quantity} و قیمت واحد {unit_price} تومان به فاکتور اضافه شد.') 175 | except (IndexError, ValueError): 176 | await update.message.reply_text("فرمت وارد شده صحیح نیست. لطفاً به شکل صحیح وارد کنید: نازل: تعداد-قیمت واحد") 177 | 178 | async def generate_invoice(update, context): 179 | user_id = update.effective_user.id 180 | items = context.user_data.get('items', []) 181 | if not items: 182 | await update.message.reply_text("هیچ آیتمی برای صدور فاکتور وجود ندارد.") 183 | return 184 | file_path = 'invoice.pdf' 185 | generate_invoice_pdf(file_path, items, user_id) 186 | await update.message.reply_document(document=open(file_path, 'rb')) 187 | 188 | # تابع اصلی 189 | def main(): 190 | # توکن بات خود را اینجا وارد کنید 191 | application = Application.builder().token("YourToken").build() 192 | 193 | # اضافه کردن هندلرها 194 | application.add_handler(CommandHandler("start", start)) 195 | application.add_handler(MessageHandler(filters.CONTACT, contact_handler)) 196 | application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_store_info)) 197 | application.add_handler(MessageHandler(filters.Regex('^افزودن آیتم$'), handle_add_item)) 198 | application.add_handler(MessageHandler(filters.Regex('^صدور فاکتور$'), generate_invoice)) 199 | 200 | # شروع بات 201 | application.run_polling() 202 | 203 | if __name__ == '__main__': 204 | main() 205 | --------------------------------------------------------------------------------