Инициализация репозитория
This commit is contained in:
commit
e32e9a1bf4
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.env
|
||||
venv/
|
||||
__pycache__/
|
||||
img/
|
||||
BIN
EXAMPLE_DB.db
Normal file
BIN
EXAMPLE_DB.db
Normal file
Binary file not shown.
85
db.py
Normal file
85
db.py
Normal file
@ -0,0 +1,85 @@
|
||||
import sqlite3
|
||||
|
||||
class BotDB:
|
||||
def __init__(self, db_file: str):
|
||||
self.conn = sqlite3.connect(db_file)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
self.cursor.execute('''CREATE TABLE IF NOT EXISTS "categories" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"name" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);''')
|
||||
self.cursor.execute('''CREATE TABLE IF NOT EXISTS "products" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"name" TEXT NOT NULL,
|
||||
"category_id" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"price" REAL NOT NULL,
|
||||
"image_path" TEXT,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("category_id") REFERENCES "categories"("id")
|
||||
);''')
|
||||
self.cursor.execute('''CREATE TABLE IF NOT EXISTS "orders" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"product_id" INTEGER NOT NULL,
|
||||
"order_date" TEXT NOT NULL,
|
||||
"delivery_date" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("user_id") REFERENCES "users"("id"),
|
||||
FOREIGN KEY("product_id") REFERENCES "products"("id")
|
||||
);''')
|
||||
self.cursor.execute('''CREATE TABLE IF NOT EXISTS "users" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"user_telegram_id" INTEGER NOT NULL,
|
||||
"nickname" TEXT NOT NULL,
|
||||
"phone_number" INTEGER NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);''')
|
||||
|
||||
def user_exists(self, user_id: int):
|
||||
result = self.cursor.execute("SELECT id FROM users WHERE user_telegram_id = ?", (user_id,))
|
||||
return bool(len(result.fetchall()))
|
||||
|
||||
def add_user(self, user_id: int, nickname: str, phone_number: int):
|
||||
self.cursor.execute("INSERT INTO users (user_telegram_id, nickname, phone_number) VALUES(?, ?, ?)", (user_id, nickname, phone_number))
|
||||
return self.conn.commit()
|
||||
|
||||
def get_categories(self):
|
||||
result = self.cursor.execute("SELECT name FROM categories").fetchall()
|
||||
lst = []
|
||||
for i in result:
|
||||
lst.append(i[0])
|
||||
return lst
|
||||
|
||||
def get_category_id(self, category: str):
|
||||
result = self.cursor.execute("SELECT id FROM categories WHERE name = ?", (category,)).fetchall()
|
||||
return result[0]
|
||||
|
||||
def get_products_in_category(self, category_id: int):
|
||||
result = self.cursor.execute("SELECT name FROM products WHERE category_id = ?", (category_id)).fetchall()
|
||||
lst = []
|
||||
for i in result:
|
||||
lst.append(i[0])
|
||||
return lst
|
||||
|
||||
def get_product_id(self, product_name: str):
|
||||
return self.cursor.execute("SELECT id FROM products WHERE name = ?", (product_name,)).fetchall()[0]
|
||||
|
||||
def get_product(self, product_id: int):
|
||||
return {
|
||||
'id': product_id[0],
|
||||
'name': self.cursor.execute("SELECT name FROM products WHERE id = ?", (product_id)).fetchall()[0][0],
|
||||
'category_id': self.cursor.execute("SELECT category_id FROM products WHERE id = ?", (product_id)).fetchall()[0][0],
|
||||
'description': self.cursor.execute("SELECT description FROM products WHERE id = ?", (product_id)).fetchall()[0][0],
|
||||
'price': self.cursor.execute("SELECT price FROM products WHERE id = ?", (product_id)).fetchall()[0][0],
|
||||
'image_path': self.cursor.execute("SELECT image_path FROM products WHERE id = ?", (product_id)).fetchall()[0][0]
|
||||
}
|
||||
|
||||
def add_order(self, user_id: int, product_id: int, order_date: str, delivery_date: str):
|
||||
self.cursor.execute("INSERT INTO orders (user_id, product_id, order_date, delivery_date) VALUES(?, ?, ?, ?)", (user_id, product_id, order_date, delivery_date))
|
||||
return self.conn.commit()
|
||||
|
||||
def close(self):
|
||||
self.conn.close()
|
||||
3
exemple.env
Normal file
3
exemple.env
Normal file
@ -0,0 +1,3 @@
|
||||
API_TOKEN=YOUR_SUPER_SECRET_TOKEN
|
||||
DATABASE_FILE_PATH=INPUT_YOUR_DATABASE_FILE_PATH
|
||||
IMGS=FOLDER_WITH_IMAGES
|
||||
125
main.py
Normal file
125
main.py
Normal file
@ -0,0 +1,125 @@
|
||||
import os
|
||||
import asyncio
|
||||
from datetime import date
|
||||
from dotenv import load_dotenv
|
||||
from aiogram import Bot, Dispatcher, executor, types
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||
from aiogram.dispatcher.filters.state import State, StatesGroup
|
||||
import logging
|
||||
|
||||
from db import BotDB
|
||||
|
||||
load_dotenv()
|
||||
TOKEN = os.getenv('API_TOKEN')
|
||||
bot = Bot(token=TOKEN)
|
||||
storage = MemoryStorage()
|
||||
dp = Dispatcher(bot, storage=storage)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class StateMachine(StatesGroup):
|
||||
phone_number = State()
|
||||
menu = State()
|
||||
products = State() # Список из категории
|
||||
product = State() # Описание товара
|
||||
orderAcception = State()
|
||||
order = State()
|
||||
|
||||
# Старт
|
||||
@dp.message_handler(commands="start", state='*')
|
||||
async def cmd_start(message: types.Message):
|
||||
user = message.from_user
|
||||
if db.user_exists(user.id):
|
||||
await message.answer(f"Здравствуйте, {user.first_name}!\nДавно не видились!")
|
||||
await StateMachine.menu.set()
|
||||
await cmd_menu(message)
|
||||
else:
|
||||
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True)
|
||||
keyboard.add(types.KeyboardButton("Отправить контакт", request_contact=True))
|
||||
await message.answer(f"Здравствуйте, {user.first_name}!")
|
||||
await message.answer("Пожалуйста зарегистрируйтесь, чтобы пользоваться ботом. Для регистрации требуется номер телефона.", reply_markup=keyboard)
|
||||
await StateMachine.phone_number.set()
|
||||
|
||||
# Регистрация пользователя через номер телефона
|
||||
@dp.message_handler(content_types=types.ContentType.CONTACT, state=StateMachine.phone_number)
|
||||
async def get_contact(message: types.Message):
|
||||
user = message.from_user
|
||||
keyboard = types.ReplyKeyboardRemove()
|
||||
db.add_user(user.id, user.username, message.contact.phone_number)
|
||||
await message.answer("Спасибо за регистрацию, давайте приступим к покупкам.", reply_markup=keyboard)
|
||||
await StateMachine.menu.set()
|
||||
await cmd_menu(message)
|
||||
|
||||
@dp.message_handler(content_types=types.ContentType.TEXT, state=StateMachine.phone_number)
|
||||
async def get_contact_from_text(message: types.Message):
|
||||
user = message.from_user
|
||||
keyboard = types.ReplyKeyboardRemove()
|
||||
db.add_user(user.id, user.username, int(message.text))
|
||||
await message.answer("Спасибо за регистрацию, давайте приступим к покупкам.", reply_markup=keyboard)
|
||||
await StateMachine.menu.set()
|
||||
await cmd_menu(message)
|
||||
|
||||
# Каталог товаров
|
||||
@dp.message_handler(commands="menu")
|
||||
async def cmd_menu(message: types.Message):
|
||||
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True)
|
||||
categories = db.get_categories()
|
||||
for name in categories:
|
||||
keyboard.add(name)
|
||||
await message.answer("Выберете категорию товаров:", reply_markup=keyboard)
|
||||
await StateMachine.products.set()
|
||||
|
||||
# Товары одной категории
|
||||
@dp.message_handler(state=StateMachine.products, content_types=types.ContentType.TEXT)
|
||||
async def send_products(message: types.Message):
|
||||
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True)
|
||||
category_id = db.get_category_id(message.text)
|
||||
products = db.get_products_in_category(category_id)
|
||||
for name in products:
|
||||
keyboard.add(name)
|
||||
await message.answer("Выберете товар:", reply_markup=keyboard)
|
||||
await StateMachine.product.set()
|
||||
|
||||
# Информация о товаре
|
||||
@dp.message_handler(state=StateMachine.product, content_types=types.ContentType.TEXT)
|
||||
async def send_product(message: types.Message, state: FSMContext):
|
||||
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True)
|
||||
keyboard.add("Заказываем")
|
||||
keyboard.add("Отмена")
|
||||
name = message.text
|
||||
product_id = db.get_product_id(name)
|
||||
product = db.get_product(product_id)
|
||||
await message.answer_photo(types.InputFile(f"{os.getenv('IMGS')}/{product.get('image_path')}"))
|
||||
await message.answer(f"Название: <b>{name}</b>\nОписание: <i>{product.get('description')}</i>\nЦена: {product.get('price')} рубелй", parse_mode=types.ParseMode.HTML, reply_markup=keyboard)
|
||||
await StateMachine.orderAcception.set()
|
||||
async with state.proxy() as data:
|
||||
data["product"] = product
|
||||
|
||||
# Подтверждение заказа
|
||||
@dp.message_handler(state=StateMachine.orderAcception, content_types=types.ContentType.TEXT)
|
||||
async def send_orderAcception(message: types.Message):
|
||||
if message.text == "Отмена":
|
||||
await message.answer("Чтобы снова использовать магазин, напишите /start.")
|
||||
else:
|
||||
await message.answer("Укажите время доставки(ДД-ММ):")
|
||||
await StateMachine.order.set()
|
||||
|
||||
# Выбор даты
|
||||
@dp.message_handler(state=StateMachine.order, content_types=types.ContentType.TEXT)
|
||||
async def send_order(message: types.Message, state: FSMContext):
|
||||
product = 0
|
||||
async with state.proxy() as data:
|
||||
product = data["product"]
|
||||
order_date = f"{date.today().year}-{date.today().month}-{date.today().day}"
|
||||
delivery_date = message.text.split('-')
|
||||
delivery_date = f"{date.today().year}-{delivery_date[1]}-{delivery_date[0]}"
|
||||
db.add_order(message.from_user.id, product.get("id"), order_date, delivery_date)
|
||||
await message.answer(f"Спасибо, что воспользовались нашим сервисом.\nЧтобы снова использовать магазин, напишите /start.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"[INFO] - Запуск бота")
|
||||
db = BotDB(os.getenv("DATABASE_FILE_PATH"))
|
||||
executor.start_polling(dp, skip_updates=True)
|
||||
db.close()
|
||||
print(f"[INFO] - База данных закрыта")
|
||||
print(f"[INFO] - Завершение работы бота")
|
||||
25
readme.md
Normal file
25
readme.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Телеграм бот для интернет покупок
|
||||
Демонстрация бота, который позволяет организовать покупку и доставку товаров.
|
||||
Данные хранятся в sqlite таблице и в папке с изображениями.
|
||||
|
||||
## Настройка бота
|
||||
Настройки включаются в себя переменные окружения описанные в файле `example.env`, которые могут быть размещены в файле `.env`;
|
||||
|
||||
## Запуск бота
|
||||
Выполнить команды:
|
||||
```shell
|
||||
pip install -r
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Как протестировать бота
|
||||
На данный момент моя копия не поднята, прошу подождать. Вы можете скачать репозиторий и сами попробовать его.
|
||||
|
||||
## ЧАВО
|
||||
*Будет ли какой-нибуть интерфейс для отслеживания товаров?*
|
||||
|
||||
Я планирую сделать эту возможность через веб-сайт.
|
||||
|
||||
*А насчёт редактирования товаров*
|
||||
|
||||
А эта функция будет реализована в самом Telegram.
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
aiogram==2.20
|
||||
python-dotenv==0.20.0
|
||||
Loading…
Reference in New Issue
Block a user