import hashlib import uuid from datetime import datetime from utilities import database_pool, handle_telegram_conversetion_exceptions from telegram import InlineKeyboardButton, InlineKeyboardMarkup import psycopg2 from private import ADMIN_CHAT_IDs from telegram.ext import ConversationHandler, MessageHandler, filters, CallbackQueryHandler confirm_remove_course = {} admin_page_keyboard = [ [InlineKeyboardButton('اضافه کردن دوره', callback_data='admin_add_course_page')], [InlineKeyboardButton('مدیریت دوره ها', callback_data='admin_course_manag')], [InlineKeyboardButton('اضافه کردن کد تخفیف', callback_data='admin_add_discount_code')] ] (GET_TITLE, GET_DESCRIPTION, GET_COVER, GET_DISCOUNT_PERCENT_PER_INVITE, GET_DISCOUNT, GET_NUMBER_OF_REFERRAL_TO_BE_FREE, GET_PRICE, GET_STATUS, GET_CONTENT, PRIVATE_CHANNEL_CHAT_ID) = range(10) EDIT_COURSE = 0 ADD_DISCOUNT_CODE_VALID_FOR_USER, ADD_DISCOUNT_CODE_CREDIT, ADD_DISCOUNT_CODE_VALID_UNTIL = range(3) def check_is_admin(func): async def wrapper(update, context): user_detail = update.effective_chat is_admin = database_pool.execute('query', {'query': f'SELECT adminID FROM Admin WHERE UserID = {user_detail.id}', 'params': None}) if is_admin or user_detail.id in ADMIN_CHAT_IDs: return await func(update, context) return return wrapper @check_is_admin async def add_admin(update, context): user_detail = update.effective_chat new_admin_user_id = context.args if not new_admin_user_id: text = 'لطفا آیدی کاربر را وارد کنید' else: try: database_pool.execute('transaction', [{'query': 'INSERT INTO Admin (userID) VALUES (%s) RETURNING *', 'params': (user_detail.id,)}]) text = 'ادمین با موفقیت اضافه شد' except psycopg2.errors.ForeignKeyViolation as fk_error: text = f'عملیات موفقیت آمیز نبود.\nخطای ForeignKey: {fk_error}' except Exception as e: text = f'عملیات موفقیت آمیز نبود.\nخطا: {e}' await context.bot.send_message(chat_id=user_detail.id, text=text, parse_mode='html') @check_is_admin async def admin_page(update, context): user_detail = update.effective_chat text = '<b>درود، به پنل ادمین خوش آمدید.\n\nلطفا بخش مورد نظر خودتون رو انتخاب کنید:</b>' await context.bot.send_message(chat_id=user_detail.id, text=text, reply_markup=InlineKeyboardMarkup(admin_page_keyboard), parse_mode='html') @check_is_admin async def add_course_page(update, context): query = update.callback_query text = '<b>محتوا دوره چیست؟</b>' add_course_page_keyboard = [ [InlineKeyboardButton('متن', callback_data='admin_add_course_text')], [InlineKeyboardButton('فایل داکیومنت', callback_data='admin_add_course_document')], [InlineKeyboardButton('فایل تصویری', callback_data='admin_add_course_photo')], [InlineKeyboardButton('فایل ویدیویی', callback_data='admin_add_course_video')], [InlineKeyboardButton('فایل صوتی', callback_data='admin_add_course_voice')], [InlineKeyboardButton('لینک کانال', callback_data='admin_add_course_chennel_link')], [InlineKeyboardButton('برگشت', callback_data='admin')]] await query.edit_message_text(text=text, reply_markup=InlineKeyboardMarkup(add_course_page_keyboard), parse_mode='html') @handle_telegram_conversetion_exceptions async def add_course_conversation(update, context): chat_id = update.effective_chat.id query = update.callback_query get_content = query.data.replace('admin_add_course_', '') context.user_data['course_content_type'] = get_content await query.answer() text = 'بسیار خب، عنوان دوره را بفرستید' await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_TITLE @handle_telegram_conversetion_exceptions async def get_course_title(update, context): chat_id = update.effective_chat.id context.user_data['course_title'] = update.message.text text = "حالا توضیحات دوره را بفرستید" await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_DESCRIPTION @handle_telegram_conversetion_exceptions async def get_course_description(update, context): chat_id = update.effective_chat.id context.user_data['course_description'] = update.message.text text = "کاور دوره را بفرستید، این میتواند عکس یا ویدیو باشد." await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_COVER @handle_telegram_conversetion_exceptions async def get_cover(update, context): chat_id = update.effective_chat.id if update.message.photo: file_id = update.message.photo[-1].file_id course_cover_type = 'photo' elif update.message.video: file_id = update.message.video.file_id course_cover_type = 'video' else: await context.bot.send_message(chat_id=chat_id, text='فرمت درست نبود، عملیات کنسل شد!', parse_mode='html') return ConversationHandler.END file = await context.bot.get_file(file_id) text = "حالا قیمت دوره را بفرستید، 0 اگر دوره رایگان است" context.user_data['course_cover_type'] = course_cover_type context.user_data['course_cover_file'] = file await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_PRICE @handle_telegram_conversetion_exceptions async def get_course_price(update, context): chat_id = update.effective_chat.id context.user_data['course_price'] = int(update.message.text) text = "کاربر باید چند نفر را به ربات دعوت کرده باشد تا این دوره را رایگان دریافت کند؟ برای غیرفعال بودن این ویژگی 0 را بفرستید." await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_NUMBER_OF_REFERRAL_TO_BE_FREE @handle_telegram_conversetion_exceptions async def get_course_number_of_referral(update, context): chat_id = update.effective_chat.id context.user_data['course_number_of_referral'] = int(update.message.text) text = "میخواهید کاربر به ازای هر یک دعوت یک درصد تخفیف بگیرد؟\n1 برای روشن بودن و 0 برای خاموش بود این حالت" await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_DISCOUNT_PERCENT_PER_INVITE @handle_telegram_conversetion_exceptions async def get_discount_percent_per_invite(update, context): chat_id = update.effective_chat.id context.user_data['course_discount_percent_per_invite'] = int(update.message.text) text = "این دوره چقدر تخفیف دارد؟ 0 برای بدون تخفیف" await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_DISCOUNT @handle_telegram_conversetion_exceptions async def get_course_discount(update, context): chat_id = update.effective_chat.id context.user_data['course_discount'] = int(update.message.text) text = "اگر دوره در کانال پرایوت با accept join است، آیدی چنل پرایوت را بفرستید. در غیر این صورت 0 را بفرستید." await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return PRIVATE_CHANNEL_CHAT_ID @handle_telegram_conversetion_exceptions async def get_channel_chat_id(update, context): chat_id = update.effective_chat.id context.user_data['private_channel_chat_id'] = update.message.text text = "وضعیت دوره را بفرستید.\n0: غیرفعال\n1: فعال" await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_STATUS @handle_telegram_conversetion_exceptions async def get_course_status(update, context): chat_id = update.effective_chat.id user_text = update.message.text if int(user_text): status = True else: status = False context.user_data['course_status'] = status text = "حالا محتوا دوره یا لینک را بفرستید." await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return GET_CONTENT @handle_telegram_conversetion_exceptions async def get_course_media(update, context): chat_id = update.effective_chat.id course_content_type = context.user_data['course_content_type'] course_title = context.user_data['course_title'] course_description = context.user_data['course_description'] course_price = context.user_data['course_price'] course_number_of_referral = context.user_data['course_number_of_referral'] course_discount = context.user_data['course_discount'] course_status = context.user_data['course_status'] channel_chat_id = context.user_data['private_channel_chat_id'] course_cover_type = context.user_data['course_cover_type'] course_cover = await context.user_data['course_cover_file'].download_as_bytearray() discount_percent_per_invite = context.user_data['course_discount_percent_per_invite'] file_id, media, channel_link = None, None, None if course_content_type == 'document': file_id = update.message.document.file_id elif course_content_type == 'photo': file_id = update.message.photo[-1].file_id elif course_content_type == 'voice': file_id = update.message.voice.file_id elif course_content_type == 'video': file_id = update.message.video.file_id elif course_content_type == 'text': media = update.message.text.encode('utf-8') elif course_content_type == 'chennel_link': channel_link = str(update.message.text) else: await context.bot.send_message(chat_id=chat_id, text="نوع فایل قابل قبول نیست!") return if file_id: file = await context.bot.get_file(file_id) media = await file.download_as_bytearray() database_pool.execute('transaction', [ {'query': 'INSERT INTO Course (status, content_type, title, description, cover_type, cover, media, channel_link, channel_chat_id, discount_percent_per_invite, price, referral_requirement, discount_percent) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING *', 'params': (course_status, course_content_type, course_title, course_description, course_cover_type, course_cover, media, channel_link, channel_chat_id, discount_percent_per_invite, course_price, course_number_of_referral, course_discount)}]) await context.bot.send_message(chat_id=chat_id, text="دوره با موفقیت ذخیره شد!") return ConversationHandler.END cource_handler = ConversationHandler( entry_points=[CallbackQueryHandler(add_course_conversation, pattern=r'admin_add_course_(.*)')], states={ GET_TITLE: [MessageHandler(filters.TEXT, get_course_title)], GET_DESCRIPTION: [MessageHandler(filters.TEXT, get_course_description)], GET_COVER: [MessageHandler(filters.ALL, get_cover)], GET_PRICE: [MessageHandler(filters.TEXT, get_course_price)], GET_NUMBER_OF_REFERRAL_TO_BE_FREE: [MessageHandler(filters.TEXT, get_course_number_of_referral)], GET_DISCOUNT_PERCENT_PER_INVITE: [MessageHandler(filters.TEXT, get_discount_percent_per_invite)], GET_DISCOUNT: [MessageHandler(filters.TEXT, get_course_discount)], PRIVATE_CHANNEL_CHAT_ID: [MessageHandler(filters.TEXT, get_channel_chat_id)], GET_STATUS: [MessageHandler(filters.TEXT, get_course_status)], GET_CONTENT: [MessageHandler(filters.ALL, get_course_media)] }, fallbacks=[], per_chat=True, allow_reentry=True, conversation_timeout=1500, ) @check_is_admin async def admin_all_course(update, context): query = update.callback_query text = '<b>دوره مورد نظر را انتخاب کنید:</b>' get_all_course = database_pool.execute('query', {'query': f'SELECT courseID,title FROM Course'}) add_course_page_keyboard = [[InlineKeyboardButton(course[1], callback_data=f'admin_manage_course_{course[0]}')] for course in get_all_course] add_course_page_keyboard.append([InlineKeyboardButton('برگشت', callback_data='admin')]) await query.edit_message_text(text=text, reply_markup=InlineKeyboardMarkup(add_course_page_keyboard), parse_mode='html') @check_is_admin async def admin_manage_course(update, context): chat_id = update.effective_chat.id query = update.callback_query course_id = query.data.replace('admin_manage_course_', '') get_course_detail = database_pool.execute('query', {'query': f'SELECT title,content_type,description,referral_requirement,price,' f'discount_percent,cover_type,cover,discount_percent_per_invite ' f'FROM Course WHERE courseID = {course_id}'})[0] if get_course_detail: keyboard = [ [InlineKeyboardButton("دریافت دوره", callback_data=f"send_course_to_user_{course_id}_0")], [InlineKeyboardButton("تغییر تایتل", callback_data=f"admin_change&title&{course_id}"), InlineKeyboardButton("تغییر توضیحات", callback_data=f"admin_change&description&{course_id}")], [InlineKeyboardButton("تغییر رفرال موردنیاز برای دریافت رایگان", callback_data=f"admin_change&referral_requirement&{course_id}")], [InlineKeyboardButton("تغییر قیمت", callback_data=f"admin_change&price&{course_id}"), InlineKeyboardButton("تغییر نوع کاور", callback_data=f"admin_change&cover_type&{course_id}")], [InlineKeyboardButton("تغییر کاور", callback_data=f"admin_change&cover&{course_id}")], [InlineKeyboardButton("تغییر درصد تخفیف", callback_data=f"admin_change&discount_percent&{course_id}")], [InlineKeyboardButton("تغییر وضعیت تخفیف به ازای اینوایت", callback_data=f"admin_change&discount_percent_per_invite&{course_id}")], [InlineKeyboardButton("تغییر نوع محتوا (text, document, photo, video, voice)", callback_data=f"admin_change&content_type&{course_id}")], [InlineKeyboardButton("تغییر محتوا", callback_data=f"admin_change&media&{course_id}")], [InlineKeyboardButton("تغییر لینک چنل پرایوت", callback_data=f"admin_change&channel_link&{course_id}"), InlineKeyboardButton("تغییر آیدی چنل پرایوت", callback_data=f"admin_change&channel_chat_id&{course_id}")], [InlineKeyboardButton("تغییر وضعیت نمایش (True or False)", callback_data=f"admin_change&status&{course_id}")], [InlineKeyboardButton("حذف دوره", callback_data=f"admin_remove_course_{course_id}")], [InlineKeyboardButton("برگشت", callback_data="course_list_")]] text = (f"نام دوره: {get_course_detail[0]}" f"\n\nتوضیحات:\n {get_course_detail[2]}" f"\n\nقیمت: {get_course_detail[4]:,}" f"\nتخفیف: {get_course_detail[5]}" f"\nنوع محتوا: {get_course_detail[1]}" f"\nتعداد رفرال مورد نیاز برای رایگان بودن دوره: {get_course_detail[3]}" f"\nمدل کاور: {get_course_detail[6]}" f"\nفعال بودن یک درصد تخفیف به ازای هر اینوایت: {get_course_detail[8]}" f"\n\nاگر کاور شما درحال حاضر ویدیو است و میخواهید آن را تبدیل به عکس کنید، ابتدا با گزینه تغییر نوع کاور این کار را انجام دهید. ورودی های مجاز photo و video است." ) cover_type = get_course_detail[6] cover = get_course_detail[7].tobytes() try: if cover_type == 'photo': await query.delete_message() await context.bot.send_photo(chat_id, photo=cover, caption=text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode='html') elif cover_type == 'video': await query.delete_message() await context.bot.send_video(chat_id, video=cover, caption=text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode='html') else: await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode='html') except Exception as e: print(e) await context.bot.send_message(chat_id, text='مشکلی در ارسال کاور وجود داشت!\n\n' + text, parse_mode='html', reply_markup=InlineKeyboardMarkup(keyboard)) return else: await query.edit_message_text('<b>• این دوره در دسترس نیست!</b>', parse_mode='html') @check_is_admin async def admin_remove_course(update, context): query = update.callback_query get_course_id = int(query.data.replace('admin_remove_course_', '')) if get_course_id not in confirm_remove_course: confirm_remove_course[get_course_id] = 1 await query.answer('آیا از حذف این دوره مطمئن هستید؟\nبرای تایید دوباره گزینه حذف را بزنید.', show_alert=True) return is_delete_done = database_pool.execute('transaction', [{'query': f'DELETE FROM Course WHERE courseID = {get_course_id} RETURNING *', 'params': None}]) if is_delete_done: text = 'دوره با موقفیت حذف شد!' else: text = 'در حذف دوره مشکلی وجود داشت' keyboard = [[InlineKeyboardButton('برگشت', callback_data='admin')]] await query.edit_message_text(text=text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode='html') @handle_telegram_conversetion_exceptions async def admin_change(update, context): chat_id = update.effective_chat.id query = update.callback_query split_data = query.data.split('&') context.user_data['admin_change_course_id'] = split_data[2] context.user_data['admin_change_column'] = split_data[1] query.answer() text = "بسیار خب، اطلاعات جدید را بفرستید." await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return EDIT_COURSE @handle_telegram_conversetion_exceptions async def update_course(update, context): chat_id = update.effective_chat.id course_id = context.user_data['admin_change_course_id'] column = context.user_data['admin_change_column'] file_id, media = None, None if update.message.photo: file_id = update.message.photo[-1].file_id elif update.message.video: file_id = update.message.video.file_id elif update.message.voice: file_id = update.message.voice.file_id elif update.message.document: file_id = update.message.document.file_id else: media = update.message.text if file_id: file = await context.bot.get_file(file_id) media = await file.download_as_bytearray() is_udpate_done = database_pool.execute('transaction', [{'query': f'UPDATE Course SET {column} = %s WHERE courseID = {course_id} RETURNING *', 'params': (media,)}]) if is_udpate_done: text = "تغییرات با موفقیت ذخیره شد!" else: text = 'مشکلی در آپدیت وجود داشت!' await context.bot.send_message(chat_id=chat_id, text=text) return ConversationHandler.END update_course_handler = ConversationHandler( entry_points=[CallbackQueryHandler(admin_change, pattern=r'admin_change(.*)')], states={ EDIT_COURSE: [MessageHandler(filters.ALL, update_course)], }, fallbacks=[], per_chat=True, allow_reentry=True, conversation_timeout=1500, ) @handle_telegram_conversetion_exceptions async def admin_add_discount_code(update, context): chat_id = update.effective_chat.id query = update.callback_query query.answer() text = ("این کد تخفیف برای کدام کاربران است؟" "\nآیدی آن ها را با کاما جدا کرده و بفرستید" "\nاگر میخواهید برای همه فعال باشد 0 را بفرستید.") await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return ADD_DISCOUNT_CODE_VALID_FOR_USER @handle_telegram_conversetion_exceptions async def get_discount_code_valid_for_user(update, context): chat_id = update.effective_chat.id get_users = update.message.text.replace(' ', '').split(',') context.user_data['discount_users'] = get_users text = ('این کد تا چه زمانی فعال است؟' '\nتاریخ را با این فرمت بفرستید:' '\nyy/mm/dd') await context.bot.send_message(chat_id=chat_id, text=text) return ADD_DISCOUNT_CODE_VALID_UNTIL @handle_telegram_conversetion_exceptions async def get_discount_code_valid_until(update, context): chat_id = update.effective_chat.id get_date = update.message.text context.user_data['discount_valid_until'] = get_date text = 'این کد چه مبلغی را کسر میکند؟ به تومان بفرستید.' await context.bot.send_message(chat_id=chat_id, text=text) return ADD_DISCOUNT_CODE_CREDIT @handle_telegram_conversetion_exceptions async def get_credit_and_generate(update, context): chat_id = update.effective_chat.id credit = int(update.message.text.replace(',', '')) until_date = context.user_data['discount_valid_until'] allow_users = context.user_data['discount_users'] generate_code = str(uuid.uuid4())[:8] if int(allow_users[0]) == 0 and len(allow_users) == 1: add_code = [(True, True, None, credit, until_date, generate_code)] else: add_code = [(True, False, int(user), credit, until_date, generate_code) for user in allow_users] values_template = ', '.join(['(%s, %s, %s, %s, %s, %s)'] * len(add_code)) query = f''' INSERT INTO DiscountCode (is_active, available_for_all_user, for_userID, credit, valid_until, code) VALUES {values_template} RETURNING discountID ''' params = [item for sublist in add_code for item in sublist] try: result = database_pool.execute('transaction', [{'query': query, 'params': params}]) except psycopg2.errors.DatetimeFieldOverflow: await context.bot.send_message(chat_id=chat_id, text='فرمت تایم درست نیست!', parse_mode='html') return ConversationHandler.END if result: text = ('کد تخفیف با موفقیت ساخته شد!' f'\n<code>{generate_code}</code>') else: text = 'مشکلی در ساخت کد تخفیف وجود داشت!' await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='html') return ConversationHandler.END add_discount_code = ConversationHandler( entry_points=[CallbackQueryHandler(admin_add_discount_code, pattern=r'admin_add_discount_code')], states={ ADD_DISCOUNT_CODE_VALID_FOR_USER: [MessageHandler(filters.TEXT, get_discount_code_valid_for_user)], ADD_DISCOUNT_CODE_VALID_UNTIL: [MessageHandler(filters.TEXT, get_discount_code_valid_until)], ADD_DISCOUNT_CODE_CREDIT: [MessageHandler(filters.TEXT, get_credit_and_generate)], }, fallbacks=[], per_chat=True, allow_reentry=True, conversation_timeout=1500, )