├── README.md └── src ├── bot.py ├── config.py ├── handlers ├── __init__.py ├── cart.py ├── p2p_payments.py └── user.py ├── keyboards ├── __init__.py └── inline_keyboards.py ├── messages ├── __init__.py └── p2p_messages.py ├── middlewares ├── __init__.py └── throttling.py ├── services ├── __init__.py ├── sql.py └── youmoney.py └── states ├── __init__.py └── user.py /README.md: -------------------------------------------------------------------------------- 1 | # telegram_bot 2 | -------------------------------------------------------------------------------- /src/bot.py: -------------------------------------------------------------------------------- 1 | from aiogram import Bot, Dispatcher 2 | 3 | from config import Config 4 | 5 | import asyncio 6 | 7 | bot = Bot(token=Config.token) 8 | dp = Dispatcher(bot=bot) 9 | 10 | async def main(): 11 | from handlers import dp 12 | try: 13 | await dp.start_polling() 14 | finally: 15 | await bot.session.close() 16 | 17 | if __name__ == "__main__": 18 | try: 19 | asyncio.run(main()) 20 | except (KeyboardInterrupt, SystemExit): 21 | print('Bot stopped!') 22 | -------------------------------------------------------------------------------- /src/config.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass 4 | class Config: 5 | token: str = 'BOT_TOKEN' 6 | admin_ids: int = 'ADMIN_IDS' 7 | pay_token: str = 'PAYMENT_TOKEN' 8 | token_p2p: str = 'P2P_TOKEN' 9 | -------------------------------------------------------------------------------- /src/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | from .user import dp 2 | from .p2p_payments import dp 3 | from .cart import dp 4 | 5 | -------------------------------------------------------------------------------- /src/handlers/cart.py: -------------------------------------------------------------------------------- 1 | from aiogram.types import Message, PreCheckoutQuery, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, \ 2 | LabeledPrice 3 | from aiogram.dispatcher.filters import Command 4 | from aiogram.types.message import ContentType 5 | from aiogram.utils.callback_data import CallbackData 6 | 7 | from src.services import DataBase 8 | from src.bot import dp, bot 9 | from src.config import Config 10 | 11 | cb = CallbackData('btn', 'type', 'product_id', 'category_id') 12 | db = DataBase('tgbot_database.db') 13 | 14 | async def gen_products(data, user_id): 15 | keyboard = InlineKeyboardMarkup() 16 | for i in data: 17 | count = await db.get_count_in_cart(user_id, i[1]) 18 | count = 0 if not count else sum(j[0] for j in count) 19 | keyboard.add(InlineKeyboardButton(text=f'{i[2]}: {i[3]}p - {count}шт', 20 | callback_data=f'btn:plus:{i[1]}:{i[5]}')) 21 | keyboard.add(InlineKeyboardButton(text='🔽', callback_data=f'btn:minus:{i[1]}:{i[5]}'), 22 | InlineKeyboardButton(text='🔼', callback_data=f'btn:plus:{i[1]}:{i[5]}'), 23 | InlineKeyboardButton(text='❌', callback_data=f'btn:del:{i[1]}:{i[5]}')) 24 | keyboard.add(InlineKeyboardButton(text='Назад', callback_data=f'btn:back:-:-')) 25 | 26 | return keyboard 27 | 28 | @dp.message_handler(Command('shop')) 29 | async def shop(message: Message): 30 | data = await db.get_categories() 31 | keyboard = InlineKeyboardMarkup() 32 | for i in data: 33 | keyboard.add(InlineKeyboardButton(text=f'{i[0]}', callback_data=f'btn:category:-:{i[1]}')) 34 | 35 | await message.answer('Что хотите купить?', reply_markup=keyboard) 36 | 37 | @dp.callback_query_handler(cb.filter(type='category')) 38 | async def goods(call: CallbackQuery, callback_data: dict): 39 | data = await db.get_products(callback_data.get('category_id')) 40 | keyboard = await gen_products(data, call.message.chat.id) 41 | 42 | await call.message.edit_reply_markup(keyboard) 43 | 44 | @dp.callback_query_handler(cb.filter(type='back')) 45 | async def back(call: CallbackQuery): 46 | data = await db.get_categories() 47 | keyboard = InlineKeyboardMarkup() 48 | for i in data: 49 | keyboard.add(InlineKeyboardButton(text=f'{i[0]}', callback_data=f'btn:category:-:{i[1]}')) 50 | 51 | await call.message.edit_reply_markup(keyboard) 52 | 53 | @dp.callback_query_handler(cb.filter(type='minus')) 54 | async def minus(call: CallbackQuery, callback_data: dict): 55 | product_id = callback_data.get('product_id') 56 | count_in_cart = await db.get_count_in_cart(call.message.chat.id, product_id) 57 | if not count_in_cart or count_in_cart[0][0] == 0: 58 | await call.message.answer('Товар в корзине отсутсвует!') 59 | return 0 60 | elif count_in_cart[0][0] == 1: 61 | await db.remove_one_item(product_id, call.message.chat.id) 62 | else: 63 | await db.change_count(count_in_cart[0][0] - 1, product_id, call.message.chat.id) 64 | 65 | data = await db.get_products(callback_data.get('category_id')) 66 | keyboard = await gen_products(data, call.message.chat.id) 67 | 68 | await call.message.edit_reply_markup(keyboard) 69 | 70 | @dp.callback_query_handler(cb.filter(type='plus')) 71 | async def plus(call: CallbackQuery, callback_data: dict): 72 | product_id = callback_data.get('product_id') 73 | count_in_cart = await db.get_count_in_cart(call.message.chat.id, product_id) 74 | count_in_stock = await db.get_count_in_stock(product_id) 75 | if count_in_stock[0][0] == 0: 76 | await call.message.answer('Товара нет в наличии :(') 77 | return 0 78 | elif not count_in_cart or count_in_cart[0][0] == 0: 79 | await db.add_to_cart(call.message.chat.id, product_id) 80 | await call.message.answer('Добавил!') 81 | elif count_in_cart[0][0] < count_in_stock[0][0]: 82 | await db.change_count(count_in_cart[0][0] + 1, product_id, call.message.chat.id) 83 | else: 84 | await call.message.answer('Больше нет в наличии') 85 | return 0 86 | 87 | data = await db.get_products(callback_data.get('category_id')) 88 | keyboard = await gen_products(data, call.message.chat.id) 89 | 90 | await call.message.edit_reply_markup(keyboard) 91 | 92 | @dp.callback_query_handler(cb.filter(type='del')) 93 | async def delete(call: CallbackQuery, callback_data: dict): 94 | product_id = callback_data.get('product_id') 95 | count_in_cart = await db.get_count_in_cart(call.message.chat.id, product_id) 96 | if not count_in_cart: 97 | await call.message.answer('Товар в корзине отсутствует!') 98 | return 0 99 | else: 100 | await db.remove_one_item(product_id, call.message.chat.id) 101 | 102 | data = await db.get_products(callback_data.get('category_id')) 103 | keyboard = await gen_products(data, call.message.chat.id) 104 | 105 | await call.message.edit_reply_markup(keyboard) 106 | 107 | @dp.message_handler(Command('empty')) 108 | async def empty_cart(message: Message): 109 | await db.empty_cart(message.chat.id) 110 | await message.answer('Корзина пуста!') 111 | 112 | # @dp.callback_query_handler(cb.filter(type='buy')) 113 | # async def add_to_cart(call: CallbackQuery, callback_data: dict): 114 | # await call.answer(cache_time=30) 115 | # 116 | # user_id = call.message.chat.id 117 | # product_id = callback_data.get('id') 118 | # 119 | # await db.add_to_cart(user_id, product_id) 120 | # await call.message.answer('Добавил!') 121 | 122 | @dp.message_handler(Command('pay')) 123 | async def buy(message: Message): 124 | data = await db.get_cart(message.chat.id) 125 | new_data = [] 126 | for i in range(len(data)): 127 | new_data.append(await db.get_user_product(data[i][2])) 128 | new_data = [new_data[i][0] for i in range(len(new_data))] 129 | prices = [LabeledPrice(label=new_data[i][2]+f' x {data[i][3]}', 130 | amount=new_data[i][3]*100*data[i][3]) for i in range(len(new_data))] 131 | await bot.send_invoice(message.chat.id, 132 | title='Cart', 133 | description='Description', 134 | provider_token=Config.pay_token, 135 | currency='rub', 136 | need_email=True, 137 | prices=prices, 138 | start_parameter='example', 139 | payload='some_invoice') 140 | 141 | @dp.pre_checkout_query_handler(lambda q: True) 142 | async def checkout_process(pre_checkout_query: PreCheckoutQuery): 143 | await bot.answer_pre_checkout_query(pre_checkout_query.id, ok=True) 144 | 145 | @dp.message_handler(content_types=ContentType.SUCCESSFUL_PAYMENT) 146 | async def s_pay(message: Message): 147 | await db.empty_cart(message.chat.id) 148 | await bot.send_message(message.chat.id, 'Платеж прошел успешно!!!') 149 | -------------------------------------------------------------------------------- /src/handlers/p2p_payments.py: -------------------------------------------------------------------------------- 1 | from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 2 | from aiogram.dispatcher.filters import Command 3 | from aiogram.utils.callback_data import CallbackData 4 | 5 | from src.messages import MESSAGES 6 | from src.config import Config 7 | from src.bot import bot, dp 8 | from src.services import DataBase 9 | 10 | import string 11 | import random 12 | from yoomoney import Quickpay, Client 13 | 14 | db = DataBase('tgbot_database.db') 15 | cb = CallbackData('btn', 'action') 16 | 17 | @dp.message_handler(Command('p2p_start')) 18 | async def p2p_start(message: Message): 19 | try: 20 | await db.add_users(message.chat.id, message.chat.first_name) 21 | except Exception as e: 22 | pass 23 | finally: 24 | await message.reply('Привет!!!') 25 | 26 | @dp.message_handler(Command('p2p_buy')) 27 | async def p2p_buy(message: Message): 28 | letters_and_digits = string.ascii_lowercase + string.digits 29 | rand_string = ''.join(random.sample(letters_and_digits, 10)) 30 | quickpay = Quickpay( 31 | receiver='4100117963448557', 32 | quickpay_form='shop', 33 | targets='Test', 34 | paymentType='SB', 35 | sum=2, 36 | label=rand_string 37 | ) 38 | 39 | await db.update_label(rand_string, message.chat.id) 40 | 41 | claim_keyboard = InlineKeyboardMarkup(inline_keyboard=[[]]) 42 | claim_keyboard.add(InlineKeyboardButton(text='Перейти к оплате!', 43 | url=quickpay.redirected_url)) 44 | claim_keyboard.add(InlineKeyboardButton(text='Получить товар!', 45 | callback_data='btn:claim')) 46 | await bot.send_message(message.chat.id, 47 | MESSAGES['buy'], 48 | reply_markup=claim_keyboard) 49 | 50 | @dp.callback_query_handler(cb.filter(action='claim')) 51 | async def check_payment(call: CallbackQuery): 52 | data = await db.get_payment_status(call.message.chat.id) 53 | bought = data[0][0] 54 | label = data[0][1] 55 | if bought == 0: 56 | client = Client(Config.token_p2p) 57 | history = client.operation_history(label=label) 58 | try: 59 | operation = history.operations[-1] 60 | if operation.status == 'success': 61 | await db.update_payment_status(call.message.chat.id) 62 | await bot.send_message(call.message.chat.id, 63 | MESSAGES['successful_payment']) 64 | except Exception as e: 65 | await bot.send_message(call.message.chat.id, 66 | MESSAGES['wait_message']) 67 | 68 | else: 69 | await bot.send_message(call.message.chat.id, 70 | MESSAGES['successful_payment']) 71 | -------------------------------------------------------------------------------- /src/handlers/user.py: -------------------------------------------------------------------------------- 1 | from aiogram.types import LabeledPrice, Message, PreCheckoutQuery, ContentType, ShippingQuery, ShippingOption 2 | from aiogram.dispatcher.filters import Command 3 | 4 | from src.bot import bot, dp 5 | from src.config import Config 6 | 7 | price = [LabeledPrice(label='Ноутбук', amount=1000000)] 8 | 9 | fast_shipping_option = ShippingOption(id='fast', title='Быстрая').add(LabeledPrice('Быстрая', 50000)) 10 | 11 | @dp.message_handler(Command('start')) 12 | async def start(message: Message): 13 | await bot.send_message(message.chat.id, 'Добро пожаловать!!!') 14 | 15 | @dp.message_handler(Command('buy')) 16 | async def buy_process(message: Message): 17 | await bot.send_invoice(message.chat.id, 18 | title='Laptop', 19 | description='Description', 20 | provider_token=Config.pay_token, 21 | currency='rub', 22 | need_email=True, 23 | is_flexible=True, 24 | prices=price, 25 | start_parameter='example', 26 | payload='some_invoice') 27 | 28 | @dp.shipping_query_handler(lambda query: True) 29 | async def shipping_process(shipping_query: ShippingQuery): 30 | if shipping_query.shipping_address.country_code == 'MG': 31 | return await bot.answer_shipping_query( 32 | shipping_query.id, 33 | ok=False, 34 | error_message='Сюда не доставляем!' 35 | ) 36 | shipping_options = [ShippingOption(id='regular', 37 | title='Обычная доставка').add(LabeledPrice('Обычная доставка', 10000))] 38 | 39 | if shipping_query.shipping_address.country_code == 'RU': 40 | shipping_options.append(fast_shipping_option) 41 | 42 | await bot.answer_shipping_query( 43 | shipping_query.id, 44 | ok=True, 45 | shipping_options=shipping_options 46 | ) 47 | 48 | # @dp.pre_checkout_query_handler(lambda query: True) 49 | # async def pre_checkout_process(pre_checkout: PreCheckoutQuery): 50 | # await bot.answer_pre_checkout_query(pre_checkout.id, ok=True) 51 | # 52 | # @dp.message_handler(content_types=ContentType.SUCCESSFUL_PAYMENT) 53 | # async def successful_payment(message: Message): 54 | # await bot.send_message(message.chat.id, 'Платеж прошел успешно!') 55 | -------------------------------------------------------------------------------- /src/keyboards/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugsandfeatures/telegram_bot/dd8e2f131c1eb0f66b9943933c3f47b994e228e6/src/keyboards/__init__.py -------------------------------------------------------------------------------- /src/keyboards/inline_keyboards.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugsandfeatures/telegram_bot/dd8e2f131c1eb0f66b9943933c3f47b994e228e6/src/keyboards/inline_keyboards.py -------------------------------------------------------------------------------- /src/messages/__init__.py: -------------------------------------------------------------------------------- 1 | from .p2p_messages import MESSAGES 2 | -------------------------------------------------------------------------------- /src/messages/p2p_messages.py: -------------------------------------------------------------------------------- 1 | buy = """ 2 | Если вы уже покупали товар, то нажмите на кнопку "Получить товар", 3 | если нет, то нажмите на кнопку "Оплатить" 4 | """ 5 | wait_msg = """ 6 | Вы не оплатили товар или оплата еще в пути! 7 | """ 8 | successful_payment = """ 9 | Большое спасибо за приобретение! 10 | """ 11 | 12 | MESSAGES = { 13 | 'successful_payment': successful_payment, 14 | 'buy': buy, 15 | 'wait_message': wait_msg, 16 | } 17 | -------------------------------------------------------------------------------- /src/middlewares/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugsandfeatures/telegram_bot/dd8e2f131c1eb0f66b9943933c3f47b994e228e6/src/middlewares/__init__.py -------------------------------------------------------------------------------- /src/middlewares/throttling.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugsandfeatures/telegram_bot/dd8e2f131c1eb0f66b9943933c3f47b994e228e6/src/middlewares/throttling.py -------------------------------------------------------------------------------- /src/services/__init__.py: -------------------------------------------------------------------------------- 1 | from .sql import DataBase 2 | -------------------------------------------------------------------------------- /src/services/sql.py: -------------------------------------------------------------------------------- 1 | from src.config import Config 2 | import sqlite3 3 | 4 | class DataBase: 5 | 6 | def __init__(self, db_file): 7 | self.connect = sqlite3.connect(db_file) 8 | self.cursor = self.connect.cursor() 9 | 10 | async def add_users(self, user_id, name): 11 | with self.connect: 12 | return self.cursor.execute("""INSERT INTO users (user_id, name, role) VALUES (?, ?, ?)""", 13 | [user_id, name, 'admin' if user_id == Config.admin_ids else 'user']) 14 | 15 | # async def update_label(self, label, user_id): 16 | # with self.connect: 17 | # return self.cursor.execute("""UPDATE users SET label=(?) WHERE user_id=(?)""", 18 | # [label, user_id]) 19 | # 20 | # async def get_payment_status(self, user_id): 21 | # with self.connect: 22 | # return self.cursor.execute("""SELECT bought, label FROM users WHERE user_id=(?)""", 23 | # [user_id]).fetchall() 24 | # 25 | # async def update_payment_status(self, user_id): 26 | # with self.connect: 27 | # return self.cursor.execute("""UPDATE users SET bought=(?) WHERE user_id=(?)""", 28 | # [True, user_id]) 29 | 30 | async def get_products(self, category_id): 31 | with self.connect: 32 | return self.cursor.execute("""SELECT * FROM products WHERE category_id=(?)""", [category_id]).fetchall() 33 | 34 | async def get_user_product(self, product_id): 35 | with self.connect: 36 | return self.cursor.execute("""SELECT * FROM products WHERE product_id=(?)""", [product_id]).fetchall() 37 | 38 | async def get_cart(self, user_id): 39 | with self.connect: 40 | return self.cursor.execute("""SELECT * FROM cart WHERE user_id=(?)""", [user_id]).fetchall() 41 | 42 | async def add_to_cart(self, user_id, product_id): 43 | with self.connect: 44 | return self.cursor.execute("""INSERT INTO cart (user_id, product_id, count) VALUES (?, ?, ?)""", 45 | [user_id, product_id, 1]) 46 | 47 | async def empty_cart(self, user_id): 48 | with self.connect: 49 | return self.cursor.execute("""DELETE FROM cart WHERE user_id=(?)""", [user_id]) 50 | 51 | async def get_categories(self): 52 | with self.connect: 53 | return self.cursor.execute("""SELECT * FROM categories""").fetchall() 54 | 55 | async def get_count_in_cart(self, user_id, product_id): 56 | with self.connect: 57 | return self.cursor.execute("""SELECT count FROM cart WHERE user_id=(?) AND product_id=(?)""", 58 | [user_id, product_id]).fetchall() 59 | 60 | async def get_count_in_stock(self, product_id): 61 | with self.connect: 62 | return self.cursor.execute("""SELECT count FROM products WHERE product_id=(?)""", 63 | [product_id]).fetchall() 64 | 65 | async def remove_one_item(self, product_id, user_id): 66 | with self.connect: 67 | return self.cursor.execute("""DELETE FROM cart WHERE product_id=(?) AND user_id=(?)""", 68 | [product_id, user_id]) 69 | 70 | async def change_count(self, count, product_id, user_id): 71 | with self.connect: 72 | return self.cursor.execute("""UPDATE cart SET count=(?) WHERE product_id=(?) AND user_id=(?)""", 73 | [count, product_id, user_id]) 74 | -------------------------------------------------------------------------------- /src/services/youmoney.py: -------------------------------------------------------------------------------- 1 | from yoomoney import Authorize 2 | 3 | Authorize( 4 | client_id="36DC67D8360C966622329EAD5B4A9E2ACE47DB7E10902B35F435B226C3BBE55A", 5 | redirect_uri="https://t.me/teg_test_idea_bot", 6 | scope=["account-info", 7 | "operation-history", 8 | "operation-details", 9 | "incoming-transfers", 10 | "payment-p2p", 11 | "payment-shop", 12 | ] 13 | ) 14 | 15 | -------------------------------------------------------------------------------- /src/states/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugsandfeatures/telegram_bot/dd8e2f131c1eb0f66b9943933c3f47b994e228e6/src/states/__init__.py -------------------------------------------------------------------------------- /src/states/user.py: -------------------------------------------------------------------------------- 1 | from aiogram.dispatcher.filters.state import StatesGroup, State 2 | 3 | class UserStates(StatesGroup): 4 | state1 = State() 5 | --------------------------------------------------------------------------------