Инициализация репозитория

This commit is contained in:
Ivan Zinchenko 2022-04-19 01:50:49 +05:00
commit e32e9a1bf4
7 changed files with 244 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.env
venv/
__pycache__/
img/

BIN
EXAMPLE_DB.db Normal file

Binary file not shown.

85
db.py Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,25 @@
# Телеграм бот для интернет покупок
Демонстрация бота, который позволяет организовать покупку и доставку товаров.
Данные хранятся в sqlite таблице и в папке с изображениями.
## Настройка бота
Настройки включаются в себя переменные окружения описанные в файле `example.env`, которые могут быть размещены в файле `.env`;
## Запуск бота
Выполнить команды:
```shell
pip install -r
python main.py
```
## Как протестировать бота
На данный момент моя копия не поднята, прошу подождать. Вы можете скачать репозиторий и сами попробовать его.
## ЧАВО
*Будет ли какой-нибуть интерфейс для отслеживания товаров?*
Я планирую сделать эту возможность через веб-сайт.
*А насчёт редактирования товаров*
А эта функция будет реализована в самом Telegram.

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
aiogram==2.20
python-dotenv==0.20.0