old_01
14
doners/Shop-bot/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Cache files
|
||||
__pycache__/
|
||||
|
||||
# Project files
|
||||
.idea/
|
||||
*.log
|
||||
.env
|
||||
*.session
|
||||
data/config.py
|
||||
data/database.db
|
||||
|
||||
# vscode
|
||||
.vscode/
|
||||
.history/
|
||||
9
doners/Shop-bot/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM python:3.7-slim
|
||||
|
||||
WORKDIR /botname
|
||||
|
||||
COPY requirements.txt /botname/
|
||||
RUN pip install -r /botname/requirements.txt
|
||||
COPY . /botname/
|
||||
|
||||
CMD python3 /botname/app.py
|
||||
21
doners/Shop-bot/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Hagai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
47
doners/Shop-bot/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
<p align="center">
|
||||
<a href="https://t.me/example_store_bot"><img src="data/assets/logo.png" alt="ShopBot"></a>
|
||||
</p>
|
||||
|
||||
This is an example Telegram shop bot. It's a simple and, most importantly, efficient way to place an order without leaving your favorite messenger.
|
||||
|
||||
## What can it do?
|
||||
|
||||
1. `/start` - needed to start the bot and choose the mode (user/admin).
|
||||
|
||||
2. `/menu` - go to the menu.
|
||||
|
||||
3. `/sos` - ask the administrator a question.
|
||||
|
||||
## Menu
|
||||
|
||||
The user menu looks like this:
|
||||
|
||||

|
||||
|
||||
## Catalog
|
||||
|
||||
The catalog consists of products sorted by categories. Users can add items to their cart, and the admin has full control over catalog management (addition/removal).
|
||||
|
||||
## Cart
|
||||
|
||||
The ordering process looks like this: the user goes to the `🛍️ Catalog`, selects the desired category, chooses products, and clicks the `🛒 Cart` button.
|
||||
|
||||

|
||||
|
||||
------
|
||||
|
||||
Then, after making sure everything is in place, proceed to checkout by clicking `📦 Place Order`.
|
||||
|
||||

|
||||
|
||||
## Add a Product
|
||||
|
||||
To add a product, select a category and click the `➕ Add Product` button. Then, fill out the "name-description-image-price" form and confirm.
|
||||
|
||||

|
||||
|
||||
## Contacting Administration
|
||||
|
||||
To ask the admin a question, simply select the `/sos` command. There is a limit on the number of questions.
|
||||
|
||||

|
||||
0
doners/Shop-bot/__init__.py
Normal file
92
doners/Shop-bot/app.py
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
import os
|
||||
import handlers
|
||||
from aiogram import executor, types
|
||||
from aiogram.types import ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||
from data import config
|
||||
from loader import dp, db, bot
|
||||
import filters
|
||||
import logging
|
||||
|
||||
filters.setup(dp)
|
||||
|
||||
WEBAPP_HOST = "0.0.0.0"
|
||||
WEBAPP_PORT = int(os.environ.get("PORT", 5000))
|
||||
user_message = 'Пользователь'
|
||||
admin_message = 'Админ'
|
||||
|
||||
|
||||
@dp.message_handler(commands='start')
|
||||
async def cmd_start(message: types.Message):
|
||||
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True)
|
||||
|
||||
markup.row(user_message, admin_message)
|
||||
|
||||
await message.answer('''Привет! 👋
|
||||
|
||||
🤖 Я бот-магазин по подаже товаров любой категории.
|
||||
|
||||
🛍️ Чтобы перейти в каталог и выбрать приглянувшиеся товары возпользуйтесь командой /menu.
|
||||
|
||||
💰 Пополнить счет можно через Яндекс.кассу, Сбербанк или Qiwi.
|
||||
|
||||
❓ Возникли вопросы? Не проблема! Команда /sos поможет связаться с админами, которые постараются как можно быстрее откликнуться.
|
||||
|
||||
🤝 Заказать похожего бота? Свяжитесь с разработчиком <a href="https://t.me/NikolaySimakov">Nikolay Simakov</a>, он не кусается)))
|
||||
''', reply_markup=markup)
|
||||
|
||||
|
||||
@dp.message_handler(text=user_message)
|
||||
async def user_mode(message: types.Message):
|
||||
|
||||
cid = message.chat.id
|
||||
if cid in config.ADMINS:
|
||||
config.ADMINS.remove(cid)
|
||||
|
||||
await message.answer('Включен пользовательский режим.', reply_markup=ReplyKeyboardRemove())
|
||||
|
||||
|
||||
@dp.message_handler(text=admin_message)
|
||||
async def admin_mode(message: types.Message):
|
||||
|
||||
cid = message.chat.id
|
||||
if cid not in config.ADMINS:
|
||||
config.ADMINS.append(cid)
|
||||
|
||||
await message.answer('Включен админский режим.', reply_markup=ReplyKeyboardRemove())
|
||||
|
||||
|
||||
async def on_startup(dp):
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
db.create_tables()
|
||||
|
||||
await bot.delete_webhook()
|
||||
await bot.set_webhook(config.WEBHOOK_URL)
|
||||
|
||||
|
||||
async def on_shutdown():
|
||||
logging.warning("Shutting down..")
|
||||
await bot.delete_webhook()
|
||||
await dp.storage.close()
|
||||
await dp.storage.wait_closed()
|
||||
logging.warning("Bot down")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if "HEROKU" in list(os.environ.keys()):
|
||||
|
||||
executor.start_webhook(
|
||||
dispatcher=dp,
|
||||
webhook_path=config.WEBHOOK_PATH,
|
||||
on_startup=on_startup,
|
||||
on_shutdown=on_shutdown,
|
||||
skip_updates=True,
|
||||
host=WEBAPP_HOST,
|
||||
port=WEBAPP_PORT,
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
executor.start_polling(dp, on_startup=on_startup, skip_updates=False)
|
||||
0
doners/Shop-bot/data/__init__.py
Normal file
BIN
doners/Shop-bot/data/assets/1.png
Normal file
|
After Width: | Height: | Size: 616 KiB |
BIN
doners/Shop-bot/data/assets/2.png
Normal file
|
After Width: | Height: | Size: 947 KiB |
BIN
doners/Shop-bot/data/assets/3.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
doners/Shop-bot/data/assets/4.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
doners/Shop-bot/data/assets/5.png
Normal file
|
After Width: | Height: | Size: 647 KiB |
BIN
doners/Shop-bot/data/assets/6.png
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
doners/Shop-bot/data/assets/7.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
doners/Shop-bot/data/assets/logo.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
doners/Shop-bot/data/assets/logo_mini.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
7
doners/Shop-bot/filters/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from aiogram import Dispatcher
|
||||
from .is_admin import IsAdmin
|
||||
from .is_user import IsUser
|
||||
|
||||
def setup(dp: Dispatcher):
|
||||
dp.filters_factory.bind(IsAdmin, event_handlers=[dp.message_handlers])
|
||||
dp.filters_factory.bind(IsUser, event_handlers=[dp.message_handlers])
|
||||
9
doners/Shop-bot/filters/is_admin.py
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
from aiogram.types import Message
|
||||
from aiogram.dispatcher.filters import BoundFilter
|
||||
from data.config import ADMINS
|
||||
|
||||
class IsAdmin(BoundFilter):
|
||||
|
||||
async def check(self, message: Message):
|
||||
return message.from_user.id in ADMINS
|
||||
9
doners/Shop-bot/filters/is_user.py
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
from aiogram.types import Message
|
||||
from aiogram.dispatcher.filters import BoundFilter
|
||||
from data.config import ADMINS
|
||||
|
||||
class IsUser(BoundFilter):
|
||||
|
||||
async def check(self, message: Message):
|
||||
return message.from_user.id not in ADMINS
|
||||
4
doners/Shop-bot/handlers/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .admin import dp
|
||||
from .user import dp
|
||||
|
||||
__all__ = ['dp']
|
||||
5
doners/Shop-bot/handlers/admin/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .add import dp
|
||||
from .questions import dp
|
||||
from .orders import dp
|
||||
|
||||
__all__ = ['dp']
|
||||
285
doners/Shop-bot/handlers/admin/add.py
Normal file
@@ -0,0 +1,285 @@
|
||||
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, ContentType, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from keyboards.default.markups import *
|
||||
from states import ProductState, CategoryState
|
||||
from aiogram.types.chat import ChatActions
|
||||
from doners.old_2.handlers import settings
|
||||
from loader import dp, db, bot
|
||||
from filters import IsAdmin
|
||||
from hashlib import md5
|
||||
|
||||
|
||||
category_cb = CallbackData('category', 'id', 'action')
|
||||
product_cb = CallbackData('product', 'id', 'action')
|
||||
|
||||
add_product = '➕ Добавить товар'
|
||||
delete_category = '🗑️ Удалить категорию'
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=settings)
|
||||
async def process_settings(message: Message):
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
|
||||
for idx, title in db.fetchall('SELECT * FROM categories'):
|
||||
|
||||
markup.add(InlineKeyboardButton(
|
||||
title, callback_data=category_cb.new(id=idx, action='view')))
|
||||
|
||||
markup.add(InlineKeyboardButton(
|
||||
'+ Добавить категорию', callback_data='add_category'))
|
||||
|
||||
await message.answer('Настройка категорий:', reply_markup=markup)
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsAdmin(), category_cb.filter(action='view'))
|
||||
async def category_callback_handler(query: CallbackQuery, callback_data: dict, state: FSMContext):
|
||||
|
||||
category_idx = callback_data['id']
|
||||
|
||||
products = db.fetchall('''SELECT * FROM products product
|
||||
WHERE product.tag = (SELECT title FROM categories WHERE idx=?)''',
|
||||
(category_idx,))
|
||||
|
||||
await query.message.delete()
|
||||
await query.answer('Все добавленные товары в эту категорию.')
|
||||
await state.update_data(category_index=category_idx)
|
||||
await show_products(query.message, products, category_idx)
|
||||
|
||||
|
||||
# category
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsAdmin(), text='add_category')
|
||||
async def add_category_callback_handler(query: CallbackQuery):
|
||||
await query.message.delete()
|
||||
await query.message.answer('Название категории?')
|
||||
await CategoryState.title.set()
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), state=CategoryState.title)
|
||||
async def set_category_title_handler(message: Message, state: FSMContext):
|
||||
|
||||
category = message.text
|
||||
idx = md5(category.encode('utf-8')).hexdigest()
|
||||
db.query('INSERT INTO categories VALUES (?, ?)', (idx, category))
|
||||
|
||||
await state.finish()
|
||||
await process_settings(message)
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=delete_category)
|
||||
async def delete_category_handler(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
if 'category_index' in data.keys():
|
||||
|
||||
idx = data['category_index']
|
||||
|
||||
db.query(
|
||||
'DELETE FROM products WHERE tag IN (SELECT title FROM categories WHERE idx=?)', (idx,))
|
||||
db.query('DELETE FROM categories WHERE idx=?', (idx,))
|
||||
|
||||
await message.answer('Готово!', reply_markup=ReplyKeyboardRemove())
|
||||
await process_settings(message)
|
||||
|
||||
|
||||
# add product
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=add_product)
|
||||
async def process_add_product(message: Message):
|
||||
|
||||
await ProductState.title.set()
|
||||
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True)
|
||||
markup.add(cancel_message)
|
||||
|
||||
await message.answer('Название?', reply_markup=markup)
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=cancel_message, state=ProductState.title)
|
||||
async def process_cancel(message: Message, state: FSMContext):
|
||||
|
||||
await message.answer('Ок, отменено!', reply_markup=ReplyKeyboardRemove())
|
||||
await state.finish()
|
||||
|
||||
await process_settings(message)
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=back_message, state=ProductState.title)
|
||||
async def process_title_back(message: Message, state: FSMContext):
|
||||
await process_add_product(message)
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), state=ProductState.title)
|
||||
async def process_title(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['title'] = message.text
|
||||
|
||||
await ProductState.next()
|
||||
await message.answer('Описание?', reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=back_message, state=ProductState.body)
|
||||
async def process_body_back(message: Message, state: FSMContext):
|
||||
|
||||
await ProductState.title.set()
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
await message.answer(f"Изменить название с <b>{data['title']}</b>?", reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), state=ProductState.body)
|
||||
async def process_body(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['body'] = message.text
|
||||
|
||||
await ProductState.next()
|
||||
await message.answer('Фото?', reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), content_types=ContentType.PHOTO, state=ProductState.image)
|
||||
async def process_image_photo(message: Message, state: FSMContext):
|
||||
|
||||
fileID = message.photo[-1].file_id
|
||||
file_info = await bot.get_file(fileID)
|
||||
downloaded_file = (await bot.download_file(file_info.file_path)).read()
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['image'] = downloaded_file
|
||||
|
||||
await ProductState.next()
|
||||
await message.answer('Цена?', reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), content_types=ContentType.TEXT, state=ProductState.image)
|
||||
async def process_image_url(message: Message, state: FSMContext):
|
||||
|
||||
if message.text == back_message:
|
||||
|
||||
await ProductState.body.set()
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
await message.answer(f"Изменить описание с <b>{data['body']}</b>?", reply_markup=back_markup())
|
||||
|
||||
else:
|
||||
|
||||
await message.answer('Вам нужно прислать фото товара.')
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), lambda message: not message.text.isdigit(), state=ProductState.price)
|
||||
async def process_price_invalid(message: Message, state: FSMContext):
|
||||
|
||||
if message.text == back_message:
|
||||
|
||||
await ProductState.image.set()
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
await message.answer("Другое изображение?", reply_markup=back_markup())
|
||||
|
||||
else:
|
||||
|
||||
await message.answer('Укажите цену в виде числа!')
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), lambda message: message.text.isdigit(), state=ProductState.price)
|
||||
async def process_price(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
data['price'] = message.text
|
||||
|
||||
title = data['title']
|
||||
body = data['body']
|
||||
price = data['price']
|
||||
|
||||
await ProductState.next()
|
||||
text = f'<b>{title}</b>\n\n{body}\n\nЦена: {price} рублей.'
|
||||
|
||||
markup = check_markup()
|
||||
|
||||
await message.answer_photo(photo=data['image'],
|
||||
caption=text,
|
||||
reply_markup=markup)
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), lambda message: message.text not in [back_message, all_right_message], state=ProductState.confirm)
|
||||
async def process_confirm_invalid(message: Message, state: FSMContext):
|
||||
await message.answer('Такого варианта не было.')
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=back_message, state=ProductState.confirm)
|
||||
async def process_confirm_back(message: Message, state: FSMContext):
|
||||
|
||||
await ProductState.price.set()
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
await message.answer(f"Изменить цену с <b>{data['price']}</b>?", reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=all_right_message, state=ProductState.confirm)
|
||||
async def process_confirm(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
title = data['title']
|
||||
body = data['body']
|
||||
image = data['image']
|
||||
price = data['price']
|
||||
|
||||
tag = db.fetchone(
|
||||
'SELECT title FROM categories WHERE idx=?', (data['category_index'],))[0]
|
||||
idx = md5(' '.join([title, body, price, tag]
|
||||
).encode('utf-8')).hexdigest()
|
||||
|
||||
db.query('INSERT INTO products VALUES (?, ?, ?, ?, ?, ?)',
|
||||
(idx, title, body, image, int(price), tag))
|
||||
|
||||
await state.finish()
|
||||
await message.answer('Готово!', reply_markup=ReplyKeyboardRemove())
|
||||
await process_settings(message)
|
||||
|
||||
|
||||
# delete product
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsAdmin(), product_cb.filter(action='delete'))
|
||||
async def delete_product_callback_handler(query: CallbackQuery, callback_data: dict):
|
||||
|
||||
product_idx = callback_data['id']
|
||||
db.query('DELETE FROM products WHERE idx=?', (product_idx,))
|
||||
await query.answer('Удалено!')
|
||||
await query.message.delete()
|
||||
|
||||
|
||||
async def show_products(m, products, category_idx):
|
||||
|
||||
await bot.send_chat_action(m.chat.id, ChatActions.TYPING)
|
||||
|
||||
for idx, title, body, image, price, tag in products:
|
||||
|
||||
text = f'<b>{title}</b>\n\n{body}\n\nЦена: {price} рублей.'
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.add(InlineKeyboardButton(
|
||||
'🗑️ Удалить', callback_data=product_cb.new(id=idx, action='delete')))
|
||||
|
||||
await m.answer_photo(photo=image,
|
||||
caption=text,
|
||||
reply_markup=markup)
|
||||
|
||||
markup = ReplyKeyboardMarkup()
|
||||
markup.add(add_product)
|
||||
markup.add(delete_category)
|
||||
|
||||
await m.answer('Хотите что-нибудь добавить или удалить?', reply_markup=markup)
|
||||
22
doners/Shop-bot/handlers/admin/orders.py
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
from aiogram.types import Message
|
||||
from loader import dp, db
|
||||
from doners.old_2.handlers import orders
|
||||
from filters import IsAdmin
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=orders)
|
||||
async def process_orders(message: Message):
|
||||
|
||||
orders = db.fetchall('SELECT * FROM orders')
|
||||
|
||||
if len(orders) == 0: await message.answer('У вас нет заказов.')
|
||||
else: await order_answer(message, orders)
|
||||
|
||||
async def order_answer(message, orders):
|
||||
|
||||
res = ''
|
||||
|
||||
for order in orders:
|
||||
res += f'Заказ <b>№{order[3]}</b>\n\n'
|
||||
|
||||
await message.answer(res)
|
||||
78
doners/Shop-bot/handlers/admin/questions.py
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
from doners.old_2.handlers import questions
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from keyboards.default.markups import all_right_message, cancel_message, submit_markup
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardRemove
|
||||
from aiogram.types.chat import ChatActions
|
||||
from states import AnswerState
|
||||
from loader import dp, db, bot
|
||||
from filters import IsAdmin
|
||||
|
||||
question_cb = CallbackData('question', 'cid', 'action')
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=questions)
|
||||
async def process_questions(message: Message):
|
||||
|
||||
await bot.send_chat_action(message.chat.id, ChatActions.TYPING)
|
||||
questions = db.fetchall('SELECT * FROM questions')
|
||||
|
||||
if len(questions) == 0:
|
||||
|
||||
await message.answer('Нет вопросов.')
|
||||
|
||||
else:
|
||||
|
||||
for cid, question in questions:
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.add(InlineKeyboardButton(
|
||||
'Ответить', callback_data=question_cb.new(cid=cid, action='answer')))
|
||||
|
||||
await message.answer(question, reply_markup=markup)
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsAdmin(), question_cb.filter(action='answer'))
|
||||
async def process_answer(query: CallbackQuery, callback_data: dict, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['cid'] = callback_data['cid']
|
||||
|
||||
await query.message.answer('Напиши ответ.', reply_markup=ReplyKeyboardRemove())
|
||||
await AnswerState.answer.set()
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), state=AnswerState.answer)
|
||||
async def process_submit(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['answer'] = message.text
|
||||
|
||||
await AnswerState.next()
|
||||
await message.answer('Убедитесь, что не ошиблись в ответе.', reply_markup=submit_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=cancel_message, state=AnswerState.submit)
|
||||
async def process_send_answer(message: Message, state: FSMContext):
|
||||
await message.answer('Отменено!', reply_markup=ReplyKeyboardRemove())
|
||||
await state.finish()
|
||||
|
||||
|
||||
@dp.message_handler(IsAdmin(), text=all_right_message, state=AnswerState.submit)
|
||||
async def process_send_answer(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
answer = data['answer']
|
||||
cid = data['cid']
|
||||
|
||||
question = db.fetchone(
|
||||
'SELECT question FROM questions WHERE cid=?', (cid,))[0]
|
||||
db.query('DELETE FROM questions WHERE cid=?', (cid,))
|
||||
text = f'Вопрос: <b>{question}</b>\n\nОтвет: <b>{answer}</b>'
|
||||
|
||||
await message.answer('Отправлено!', reply_markup=ReplyKeyboardRemove())
|
||||
await bot.send_message(cid, text)
|
||||
|
||||
await state.finish()
|
||||
8
doners/Shop-bot/handlers/user/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from .menu import dp
|
||||
from .cart import dp
|
||||
from .wallet import dp
|
||||
from .catalog import dp
|
||||
from .delivery_status import dp
|
||||
from .sos import dp
|
||||
|
||||
__all__ = ['dp']
|
||||
247
doners/Shop-bot/handlers/user/cart.py
Normal file
@@ -0,0 +1,247 @@
|
||||
import logging
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.types import Message, CallbackQuery, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from keyboards.inline.products_from_cart import product_markup, product_cb
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from keyboards.default.markups import *
|
||||
from aiogram.types.chat import ChatActions
|
||||
from states import CheckoutState
|
||||
from loader import dp, db, bot
|
||||
from filters import IsUser
|
||||
from .menu import cart
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=cart)
|
||||
async def process_cart(message: Message, state: FSMContext):
|
||||
|
||||
cart_data = db.fetchall(
|
||||
'SELECT * FROM cart WHERE cid=?', (message.chat.id,))
|
||||
|
||||
if len(cart_data) == 0:
|
||||
|
||||
await message.answer('Ваша корзина пуста.')
|
||||
|
||||
else:
|
||||
|
||||
await bot.send_chat_action(message.chat.id, ChatActions.TYPING)
|
||||
async with state.proxy() as data:
|
||||
data['products'] = {}
|
||||
|
||||
order_cost = 0
|
||||
|
||||
for _, idx, count_in_cart in cart_data:
|
||||
|
||||
product = db.fetchone('SELECT * FROM products WHERE idx=?', (idx,))
|
||||
|
||||
if product == None:
|
||||
|
||||
db.query('DELETE FROM cart WHERE idx=?', (idx,))
|
||||
|
||||
else:
|
||||
_, title, body, image, price, _ = product
|
||||
order_cost += price
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['products'][idx] = [title, price, count_in_cart]
|
||||
|
||||
markup = product_markup(idx, count_in_cart)
|
||||
text = f'<b>{title}</b>\n\n{body}\n\nЦена: {price}₽.'
|
||||
|
||||
await message.answer_photo(photo=image,
|
||||
caption=text,
|
||||
reply_markup=markup)
|
||||
|
||||
if order_cost != 0:
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||
markup.add('📦 Оформить заказ')
|
||||
|
||||
await message.answer('Перейти к оформлению?',
|
||||
reply_markup=markup)
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsUser(), product_cb.filter(action='count'))
|
||||
@dp.callback_query_handler(IsUser(), product_cb.filter(action='increase'))
|
||||
@dp.callback_query_handler(IsUser(), product_cb.filter(action='decrease'))
|
||||
async def product_callback_handler(query: CallbackQuery, callback_data: dict, state: FSMContext):
|
||||
|
||||
idx = callback_data['id']
|
||||
action = callback_data['action']
|
||||
|
||||
if 'count' == action:
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
if 'products' not in data.keys():
|
||||
|
||||
await process_cart(query.message, state)
|
||||
|
||||
else:
|
||||
|
||||
await query.answer('Количество - ' + data['products'][idx][2])
|
||||
|
||||
else:
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
if 'products' not in data.keys():
|
||||
|
||||
await process_cart(query.message, state)
|
||||
|
||||
else:
|
||||
|
||||
data['products'][idx][2] += 1 if 'increase' == action else -1
|
||||
count_in_cart = data['products'][idx][2]
|
||||
|
||||
if count_in_cart == 0:
|
||||
|
||||
db.query('''DELETE FROM cart
|
||||
WHERE cid = ? AND idx = ?''', (query.message.chat.id, idx))
|
||||
|
||||
await query.message.delete()
|
||||
else:
|
||||
|
||||
db.query('''UPDATE cart
|
||||
SET quantity = ?
|
||||
WHERE cid = ? AND idx = ?''', (count_in_cart, query.message.chat.id, idx))
|
||||
|
||||
await query.message.edit_reply_markup(product_markup(idx, count_in_cart))
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text='📦 Оформить заказ')
|
||||
async def process_checkout(message: Message, state: FSMContext):
|
||||
|
||||
await CheckoutState.check_cart.set()
|
||||
await checkout(message, state)
|
||||
|
||||
|
||||
async def checkout(message, state):
|
||||
answer = ''
|
||||
total_price = 0
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
for title, price, count_in_cart in data['products'].values():
|
||||
|
||||
tp = count_in_cart * price
|
||||
answer += f'<b>{title}</b> * {count_in_cart}шт. = {tp}₽\n'
|
||||
total_price += tp
|
||||
|
||||
await message.answer(f'{answer}\nОбщая сумма заказа: {total_price}₽.',
|
||||
reply_markup=check_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), lambda message: message.text not in [all_right_message, back_message], state=CheckoutState.check_cart)
|
||||
async def process_check_cart_invalid(message: Message):
|
||||
await message.reply('Такого варианта не было.')
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.check_cart)
|
||||
async def process_check_cart_back(message: Message, state: FSMContext):
|
||||
await state.finish()
|
||||
await process_cart(message, state)
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=all_right_message, state=CheckoutState.check_cart)
|
||||
async def process_check_cart_all_right(message: Message, state: FSMContext):
|
||||
await CheckoutState.next()
|
||||
await message.answer('Укажите свое имя.',
|
||||
reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.name)
|
||||
async def process_name_back(message: Message, state: FSMContext):
|
||||
await CheckoutState.check_cart.set()
|
||||
await checkout(message, state)
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), state=CheckoutState.name)
|
||||
async def process_name(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
data['name'] = message.text
|
||||
|
||||
if 'address' in data.keys():
|
||||
|
||||
await confirm(message)
|
||||
await CheckoutState.confirm.set()
|
||||
|
||||
else:
|
||||
|
||||
await CheckoutState.next()
|
||||
await message.answer('Укажите свой адрес места жительства.',
|
||||
reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.address)
|
||||
async def process_address_back(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
await message.answer('Изменить имя с <b>' + data['name'] + '</b>?',
|
||||
reply_markup=back_markup())
|
||||
|
||||
await CheckoutState.name.set()
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), state=CheckoutState.address)
|
||||
async def process_address(message: Message, state: FSMContext):
|
||||
|
||||
async with state.proxy() as data:
|
||||
data['address'] = message.text
|
||||
|
||||
await confirm(message)
|
||||
await CheckoutState.next()
|
||||
|
||||
|
||||
async def confirm(message):
|
||||
|
||||
await message.answer('Убедитесь, что все правильно оформлено и подтвердите заказ.',
|
||||
reply_markup=confirm_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), lambda message: message.text not in [confirm_message, back_message], state=CheckoutState.confirm)
|
||||
async def process_confirm_invalid(message: Message):
|
||||
await message.reply('Такого варианта не было.')
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.confirm)
|
||||
async def process_confirm(message: Message, state: FSMContext):
|
||||
|
||||
await CheckoutState.address.set()
|
||||
|
||||
async with state.proxy() as data:
|
||||
await message.answer('Изменить адрес с <b>' + data['address'] + '</b>?',
|
||||
reply_markup=back_markup())
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=confirm_message, state=CheckoutState.confirm)
|
||||
async def process_confirm(message: Message, state: FSMContext):
|
||||
|
||||
enough_money = True # enough money on the balance sheet
|
||||
markup = ReplyKeyboardRemove()
|
||||
|
||||
if enough_money:
|
||||
|
||||
logging.info('Deal was made.')
|
||||
|
||||
async with state.proxy() as data:
|
||||
|
||||
cid = message.chat.id
|
||||
products = [idx + '=' + str(quantity)
|
||||
for idx, quantity in db.fetchall('''SELECT idx, quantity FROM cart
|
||||
WHERE cid=?''', (cid,))] # idx=quantity
|
||||
|
||||
db.query('INSERT INTO orders VALUES (?, ?, ?, ?)',
|
||||
(cid, data['name'], data['address'], ' '.join(products)))
|
||||
|
||||
db.query('DELETE FROM cart WHERE cid=?', (cid,))
|
||||
|
||||
await message.answer('Ок! Ваш заказ уже в пути 🚀\nИмя: <b>' + data['name'] + '</b>\nАдрес: <b>' + data['address'] + '</b>',
|
||||
reply_markup=markup)
|
||||
else:
|
||||
|
||||
await message.answer('У вас недостаточно денег на счете. Пополните баланс!',
|
||||
reply_markup=markup)
|
||||
|
||||
await state.finish()
|
||||
58
doners/Shop-bot/handlers/user/catalog.py
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
import logging
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from keyboards.inline.categories import categories_markup, category_cb
|
||||
from keyboards.inline.products_from_catalog import product_markup, product_cb
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from aiogram.types.chat import ChatActions
|
||||
from loader import dp, db, bot
|
||||
from .menu import catalog
|
||||
from filters import IsUser
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=catalog)
|
||||
async def process_catalog(message: Message):
|
||||
await message.answer('Выберите раздел, чтобы вывести список товаров:',
|
||||
reply_markup=categories_markup())
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsUser(), category_cb.filter(action='view'))
|
||||
async def category_callback_handler(query: CallbackQuery, callback_data: dict):
|
||||
|
||||
products = db.fetchall('''SELECT * FROM products product
|
||||
WHERE product.tag = (SELECT title FROM categories WHERE idx=?)
|
||||
AND product.idx NOT IN (SELECT idx FROM cart WHERE cid = ?)''',
|
||||
(callback_data['id'], query.message.chat.id))
|
||||
|
||||
await query.answer('Все доступные товары.')
|
||||
await show_products(query.message, products)
|
||||
|
||||
|
||||
@dp.callback_query_handler(IsUser(), product_cb.filter(action='add'))
|
||||
async def add_product_callback_handler(query: CallbackQuery, callback_data: dict):
|
||||
|
||||
db.query('INSERT INTO cart VALUES (?, ?, 1)',
|
||||
(query.message.chat.id, callback_data['id']))
|
||||
|
||||
await query.answer('Товар добавлен в корзину!')
|
||||
await query.message.delete()
|
||||
|
||||
|
||||
async def show_products(m, products):
|
||||
|
||||
if len(products) == 0:
|
||||
|
||||
await m.answer('Здесь ничего нет 😢')
|
||||
|
||||
else:
|
||||
|
||||
await bot.send_chat_action(m.chat.id, ChatActions.TYPING)
|
||||
|
||||
for idx, title, body, image, price, _ in products:
|
||||
|
||||
markup = product_markup(idx, price)
|
||||
text = f'<b>{title}</b>\n\n{body}'
|
||||
|
||||
await m.answer_photo(photo=image,
|
||||
caption=text,
|
||||
reply_markup=markup)
|
||||
31
doners/Shop-bot/handlers/user/delivery_status.py
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
from aiogram.types import Message
|
||||
from loader import dp, db
|
||||
from .menu import delivery_status
|
||||
from filters import IsUser
|
||||
|
||||
@dp.message_handler(IsUser(), text=delivery_status)
|
||||
async def process_delivery_status(message: Message):
|
||||
|
||||
orders = db.fetchall('SELECT * FROM orders WHERE cid=?', (message.chat.id,))
|
||||
|
||||
if len(orders) == 0: await message.answer('У вас нет активных заказов.')
|
||||
else: await delivery_status_answer(message, orders)
|
||||
|
||||
async def delivery_status_answer(message, orders):
|
||||
|
||||
res = ''
|
||||
|
||||
for order in orders:
|
||||
|
||||
res += f'Заказ <b>№{order[3]}</b>'
|
||||
answer = [
|
||||
' лежит на складе.',
|
||||
' уже в пути!',
|
||||
' прибыл и ждет вас на почте!'
|
||||
]
|
||||
|
||||
res += answer[0]
|
||||
res += '\n\n'
|
||||
|
||||
await message.answer(res)
|
||||
30
doners/Shop-bot/handlers/user/menu.py
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
from aiogram.types import Message, CallbackQuery, ReplyKeyboardMarkup
|
||||
from loader import dp
|
||||
from filters import IsAdmin, IsUser
|
||||
|
||||
catalog = '🛍️ Каталог'
|
||||
balance = '💰 Баланс'
|
||||
cart = '🛒 Корзина'
|
||||
delivery_status = '🚚 Статус заказа'
|
||||
|
||||
settings = '⚙️ Настройка каталога'
|
||||
orders = '🚚 Заказы'
|
||||
questions = '❓ Вопросы'
|
||||
|
||||
@dp.message_handler(IsAdmin(), commands='menu')
|
||||
async def admin_menu(message: Message):
|
||||
markup = ReplyKeyboardMarkup(selective=True)
|
||||
markup.add(settings)
|
||||
markup.add(questions, orders)
|
||||
|
||||
await message.answer('Меню', reply_markup=markup)
|
||||
|
||||
@dp.message_handler(IsUser(), commands='menu')
|
||||
async def user_menu(message: Message):
|
||||
markup = ReplyKeyboardMarkup(selective=True)
|
||||
markup.add(catalog)
|
||||
markup.add(balance, cart)
|
||||
markup.add(delivery_status)
|
||||
|
||||
await message.answer('Меню', reply_markup=markup)
|
||||
54
doners/Shop-bot/handlers/user/sos.py
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.types import ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||
from keyboards.default.markups import all_right_message, cancel_message, submit_markup
|
||||
from aiogram.types import Message
|
||||
from states import SosState
|
||||
from filters import IsUser
|
||||
from loader import dp, db
|
||||
|
||||
|
||||
@dp.message_handler(commands='sos')
|
||||
async def cmd_sos(message: Message):
|
||||
await SosState.question.set()
|
||||
await message.answer('В чем суть проблемы? Опишите как можно детальнее и администратор обязательно вам ответит.', reply_markup=ReplyKeyboardRemove())
|
||||
|
||||
|
||||
@dp.message_handler(state=SosState.question)
|
||||
async def process_question(message: Message, state: FSMContext):
|
||||
async with state.proxy() as data:
|
||||
data['question'] = message.text
|
||||
|
||||
await message.answer('Убедитесь, что все верно.', reply_markup=submit_markup())
|
||||
await SosState.next()
|
||||
|
||||
|
||||
@dp.message_handler(lambda message: message.text not in [cancel_message, all_right_message], state=SosState.submit)
|
||||
async def process_price_invalid(message: Message):
|
||||
await message.answer('Такого варианта не было.')
|
||||
|
||||
|
||||
@dp.message_handler(text=cancel_message, state=SosState.submit)
|
||||
async def process_cancel(message: Message, state: FSMContext):
|
||||
await message.answer('Отменено!', reply_markup=ReplyKeyboardRemove())
|
||||
await state.finish()
|
||||
|
||||
|
||||
@dp.message_handler(text=all_right_message, state=SosState.submit)
|
||||
async def process_submit(message: Message, state: FSMContext):
|
||||
|
||||
cid = message.chat.id
|
||||
|
||||
if db.fetchone('SELECT * FROM questions WHERE cid=?', (cid,)) == None:
|
||||
|
||||
async with state.proxy() as data:
|
||||
db.query('INSERT INTO questions VALUES (?, ?)',
|
||||
(cid, data['question']))
|
||||
|
||||
await message.answer('Отправлено!', reply_markup=ReplyKeyboardRemove())
|
||||
|
||||
else:
|
||||
|
||||
await message.answer('Превышен лимит на количество задаваемых вопросов.', reply_markup=ReplyKeyboardRemove())
|
||||
|
||||
await state.finish()
|
||||
18
doners/Shop-bot/handlers/user/wallet.py
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
from loader import dp
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.types import Message
|
||||
from filters import IsUser
|
||||
from .menu import balance
|
||||
|
||||
# test card ==> 1111 1111 1111 1026, 12/22, CVC 000
|
||||
|
||||
# shopId 506751
|
||||
|
||||
# shopArticleId 538350
|
||||
|
||||
|
||||
@dp.message_handler(IsUser(), text=balance)
|
||||
async def process_balance(message: Message, state: FSMContext):
|
||||
await message.answer('Ваш кошелек пуст! Чтобы его пополнить нужно...')
|
||||
|
||||
2
doners/Shop-bot/keyboards/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import inline
|
||||
from . import default
|
||||
1
doners/Shop-bot/keyboards/default/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import markups
|
||||
31
doners/Shop-bot/keyboards/default/markups.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from aiogram.types import ReplyKeyboardMarkup
|
||||
|
||||
back_message = '👈 Назад'
|
||||
confirm_message = '✅ Подтвердить заказ'
|
||||
all_right_message = '✅ Все верно'
|
||||
cancel_message = '🚫 Отменить'
|
||||
|
||||
def confirm_markup():
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||
markup.add(confirm_message)
|
||||
markup.add(back_message)
|
||||
|
||||
return markup
|
||||
|
||||
def back_markup():
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||
markup.add(back_message)
|
||||
|
||||
return markup
|
||||
|
||||
def check_markup():
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||
markup.row(back_message, all_right_message)
|
||||
|
||||
return markup
|
||||
|
||||
def submit_markup():
|
||||
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||
markup.row(cancel_message, all_right_message)
|
||||
|
||||
return markup
|
||||
3
doners/Shop-bot/keyboards/inline/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . import products_from_catalog
|
||||
from . import products_from_cart
|
||||
from . import categories
|
||||
16
doners/Shop-bot/keyboards/inline/categories.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from loader import db
|
||||
|
||||
category_cb = CallbackData('category', 'id', 'action')
|
||||
|
||||
|
||||
def categories_markup():
|
||||
|
||||
global category_cb
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
for idx, title in db.fetchall('SELECT * FROM categories'):
|
||||
markup.add(InlineKeyboardButton(title, callback_data=category_cb.new(id=idx, action='view')))
|
||||
|
||||
return markup
|
||||
16
doners/Shop-bot/keyboards/inline/products_from_cart.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
|
||||
product_cb = CallbackData('product', 'id', 'action')
|
||||
|
||||
def product_markup(idx, count):
|
||||
|
||||
global product_cb
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
back_btn = InlineKeyboardButton('⬅️', callback_data=product_cb.new(id=idx, action='decrease'))
|
||||
count_btn = InlineKeyboardButton(count, callback_data=product_cb.new(id=idx, action='count'))
|
||||
next_btn = InlineKeyboardButton('➡️', callback_data=product_cb.new(id=idx, action='increase'))
|
||||
markup.row(back_btn, count_btn, next_btn)
|
||||
|
||||
return markup
|
||||
15
doners/Shop-bot/keyboards/inline/products_from_catalog.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from loader import db
|
||||
|
||||
product_cb = CallbackData('product', 'id', 'action')
|
||||
|
||||
|
||||
def product_markup(idx='', price=0):
|
||||
|
||||
global product_cb
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.add(InlineKeyboardButton(f'Добавить в корзину - {price}₽', callback_data=product_cb.new(id=idx, action='add')))
|
||||
|
||||
return markup
|
||||
10
doners/Shop-bot/loader.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from aiogram import Bot, Dispatcher, types
|
||||
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||
from utils.db.storage import DatabaseManager
|
||||
|
||||
from data import config
|
||||
|
||||
bot = Bot(token=config.BOT_TOKEN, parse_mode=types.ParseMode.HTML)
|
||||
storage = MemoryStorage()
|
||||
dp = Dispatcher(bot, storage=storage)
|
||||
db = DatabaseManager('data/database.db')
|
||||
1
doners/Shop-bot/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
aiogram==2.9.2
|
||||
3
doners/Shop-bot/states/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .checkout_state import CheckoutState
|
||||
from .product_state import ProductState, CategoryState
|
||||
from .sos_state import SosState, AnswerState
|
||||
7
doners/Shop-bot/states/checkout_state.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||
|
||||
class CheckoutState(StatesGroup):
|
||||
check_cart = State()
|
||||
name = State()
|
||||
address = State()
|
||||
confirm = State()
|
||||
11
doners/Shop-bot/states/product_state.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||
|
||||
class ProductState(StatesGroup):
|
||||
title = State()
|
||||
body = State()
|
||||
image = State()
|
||||
price = State()
|
||||
confirm = State()
|
||||
|
||||
class CategoryState(StatesGroup):
|
||||
title = State()
|
||||
9
doners/Shop-bot/states/sos_state.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||
|
||||
class SosState(StatesGroup):
|
||||
question = State()
|
||||
submit = State()
|
||||
|
||||
class AnswerState(StatesGroup):
|
||||
answer = State()
|
||||
submit = State()
|
||||
1
doners/Shop-bot/utils/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import db
|
||||
1
doners/Shop-bot/utils/db/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .storage import DatabaseManager
|
||||
59
doners/Shop-bot/utils/db/storage.py
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
import sqlite3 as lite
|
||||
|
||||
class DatabaseManager(object):
|
||||
|
||||
def __init__(self, path):
|
||||
self.conn = lite.connect(path)
|
||||
self.conn.execute('pragma foreign_keys = on')
|
||||
self.conn.commit()
|
||||
self.cur = self.conn.cursor()
|
||||
|
||||
def create_tables(self):
|
||||
self.query('CREATE TABLE IF NOT EXISTS products (idx text, title text, body text, photo blob, price int, tag text)')
|
||||
self.query('CREATE TABLE IF NOT EXISTS orders (cid int, usr_name text, usr_address text, products text)')
|
||||
self.query('CREATE TABLE IF NOT EXISTS cart (cid int, idx text, quantity int)')
|
||||
self.query('CREATE TABLE IF NOT EXISTS categories (idx text, title text)')
|
||||
self.query('CREATE TABLE IF NOT EXISTS wallet (cid int, balance real)')
|
||||
self.query('CREATE TABLE IF NOT EXISTS questions (cid int, question text)')
|
||||
|
||||
def query(self, arg, values=None):
|
||||
if values == None:
|
||||
self.cur.execute(arg)
|
||||
else:
|
||||
self.cur.execute(arg, values)
|
||||
self.conn.commit()
|
||||
|
||||
def fetchone(self, arg, values=None):
|
||||
if values == None:
|
||||
self.cur.execute(arg)
|
||||
else:
|
||||
self.cur.execute(arg, values)
|
||||
return self.cur.fetchone()
|
||||
|
||||
def fetchall(self, arg, values=None):
|
||||
if values == None:
|
||||
self.cur.execute(arg)
|
||||
else:
|
||||
self.cur.execute(arg, values)
|
||||
return self.cur.fetchall()
|
||||
|
||||
def __del__(self):
|
||||
self.conn.close()
|
||||
|
||||
|
||||
'''
|
||||
|
||||
products: idx text, title text, body text, photo blob, price int, tag text
|
||||
|
||||
orders: cid int, usr_name text, usr_address text, products text
|
||||
|
||||
cart: cid int, idx text, quantity int ==> product_idx
|
||||
|
||||
categories: idx text, title text
|
||||
|
||||
wallet: cid int, balance real
|
||||
|
||||
questions: cid int, question text
|
||||
|
||||
'''
|
||||