170600
This commit is contained in:
3
bot-market/.idea/.gitignore
generated
vendored
Normal file
3
bot-market/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
10
bot-market/.idea/bot-market.iml
generated
Normal file
10
bot-market/.idea/bot-market.iml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
bot-market/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
bot-market/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
bot-market/.idea/misc.xml
generated
Normal file
7
bot-market/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.10 (bot-market)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (bot-market)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
bot-market/.idea/modules.xml
generated
Normal file
8
bot-market/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/bot-market.iml" filepath="$PROJECT_DIR$/.idea/bot-market.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
94
bot-market/app/admin.py
Normal file
94
bot-market/app/admin.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
from aiogram import Router, F
|
||||||
|
from aiogram.types import Message, CallbackQuery
|
||||||
|
from aiogram.filters import CommandStart, Command, Filter
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
|
|
||||||
|
import app.keyboards as kb
|
||||||
|
from app.database.requests import get_users, set_item
|
||||||
|
|
||||||
|
admin = Router()
|
||||||
|
|
||||||
|
|
||||||
|
class Newsletter(StatesGroup):
|
||||||
|
message = State()
|
||||||
|
|
||||||
|
|
||||||
|
class AddItem(StatesGroup):
|
||||||
|
name = State()
|
||||||
|
category = State()
|
||||||
|
description = State()
|
||||||
|
photo = State()
|
||||||
|
price = State()
|
||||||
|
|
||||||
|
|
||||||
|
class AdminProtect(Filter):
|
||||||
|
async def __call__(self, message: Message):
|
||||||
|
return message.from_user.id in [1477217831]
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), Command('apanel'))
|
||||||
|
async def apanel(message: Message):
|
||||||
|
await message.answer('Возможные команды: /newsletter\n/add_item')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), Command('newsletter'))
|
||||||
|
async def newsletter(message: Message, state: FSMContext):
|
||||||
|
await state.set_state(Newsletter.message)
|
||||||
|
await message.answer('Отправьте сообщение, которое вы хотите разослать всем пользователям')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), Newsletter.message)
|
||||||
|
async def newsletter_message(message: Message, state: FSMContext):
|
||||||
|
await message.answer('Подождите... идёт рассылка.')
|
||||||
|
for user in await get_users():
|
||||||
|
try:
|
||||||
|
await message.send_copy(chat_id=user.tg_id)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
await message.answer('Рассылка успешно завершена.')
|
||||||
|
await state.clear()
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), Command('add_item'))
|
||||||
|
async def add_item(message: Message, state: FSMContext):
|
||||||
|
await state.set_state(AddItem.name)
|
||||||
|
await message.answer('Введите название товара')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), AddItem.name)
|
||||||
|
async def add_item_name(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(name=message.text)
|
||||||
|
await state.set_state(AddItem.category)
|
||||||
|
await message.answer('Выберите категорию товара', reply_markup=await kb.categories())
|
||||||
|
|
||||||
|
|
||||||
|
@admin.callback_query(AdminProtect(), AddItem.category)
|
||||||
|
async def add_item_category(callback: CallbackQuery, state: FSMContext):
|
||||||
|
await state.update_data(category=callback.data.split('_')[1])
|
||||||
|
await state.set_state(AddItem.description)
|
||||||
|
await callback.answer('')
|
||||||
|
await callback.message.answer('Введите описание товара')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), AddItem.description)
|
||||||
|
async def add_item_description(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(description=message.text)
|
||||||
|
await state.set_state(AddItem.photo)
|
||||||
|
await message.answer('Отправьте фото товара')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), AddItem.photo, F.photo)
|
||||||
|
async def add_item_photo(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(photo=message.photo[-1].file_id)
|
||||||
|
await state.set_state(AddItem.price)
|
||||||
|
await message.answer('Введите цену товара')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.message(AdminProtect(), AddItem.price)
|
||||||
|
async def add_item_price(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(price=message.text)
|
||||||
|
data = await state.get_data()
|
||||||
|
await set_item(data)
|
||||||
|
await message.answer('Товар успешно добавлен')
|
||||||
|
await state.clear()
|
||||||
62
bot-market/app/database/models.py
Normal file
62
bot-market/app/database/models.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from sqlalchemy import BigInteger, ForeignKey, String
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship, DeclarativeBase
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engine
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from config import ENGINE, ECHO
|
||||||
|
|
||||||
|
engine = create_async_engine(url=ENGINE)#, echo=ECHO)
|
||||||
|
|
||||||
|
async_session = async_sessionmaker(engine)
|
||||||
|
|
||||||
|
|
||||||
|
class Base(AsyncAttrs, DeclarativeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
tg_id = mapped_column(BigInteger)
|
||||||
|
|
||||||
|
basket_rel: Mapped[List['Basket']] = relationship(back_populates='user_rel')
|
||||||
|
|
||||||
|
|
||||||
|
class Category(Base):
|
||||||
|
__tablename__ = 'categories'
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(50))
|
||||||
|
|
||||||
|
item_rel: Mapped[List['Item']] = relationship(back_populates='category_rel')
|
||||||
|
|
||||||
|
|
||||||
|
class Item(Base):
|
||||||
|
__tablename__ = 'items'
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(50))
|
||||||
|
description: Mapped[str] = mapped_column(String(200))
|
||||||
|
photo: Mapped[str] = mapped_column(String(200))
|
||||||
|
price: Mapped[int] = mapped_column()
|
||||||
|
category: Mapped[int] = mapped_column(ForeignKey('categories.id'))
|
||||||
|
|
||||||
|
category_rel: Mapped['Category'] = relationship(back_populates='item_rel')
|
||||||
|
basket_rel: Mapped[List['Basket']] = relationship(back_populates='item_rel')
|
||||||
|
|
||||||
|
|
||||||
|
class Basket(Base):
|
||||||
|
__tablename__ = 'basket'
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
user: Mapped[int] = mapped_column(ForeignKey('users.id'))
|
||||||
|
item: Mapped[int] = mapped_column(ForeignKey('items.id'))
|
||||||
|
|
||||||
|
user_rel: Mapped['User'] = relationship(back_populates='basket_rel')
|
||||||
|
item_rel: Mapped['Item'] = relationship(back_populates='basket_rel')
|
||||||
|
|
||||||
|
|
||||||
|
async def async_main():
|
||||||
|
async with engine.begin() as conn:
|
||||||
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
64
bot-market/app/database/requests.py
Normal file
64
bot-market/app/database/requests.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from app.database.models import User, Category, Item, Basket
|
||||||
|
from app.database.models import async_session
|
||||||
|
|
||||||
|
from sqlalchemy import select, update, delete
|
||||||
|
|
||||||
|
|
||||||
|
async def set_user(tg_id):
|
||||||
|
async with async_session() as session:
|
||||||
|
user = await session.scalar(select(User).where(User.tg_id == tg_id))
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
session.add(User(tg_id=tg_id))
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def set_item(data):
|
||||||
|
async with async_session() as session:
|
||||||
|
session.add(Item(**data))
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def set_basket(tg_id, item_id):
|
||||||
|
async with async_session() as session:
|
||||||
|
user = await session.scalar(select(User).where(User.tg_id == tg_id))
|
||||||
|
session.add(Basket(user=user.id, item=item_id))
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_basket(tg_id):
|
||||||
|
async with async_session() as session:
|
||||||
|
user = await session.scalar(select(User).where(User.tg_id == tg_id))
|
||||||
|
basket = await session.scalars(select(Basket).where(Basket.user == user.id))
|
||||||
|
return basket
|
||||||
|
|
||||||
|
|
||||||
|
async def get_users():
|
||||||
|
async with async_session() as session:
|
||||||
|
users = await session.scalars(select(User))
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
async def get_categories():
|
||||||
|
async with async_session() as session:
|
||||||
|
categories = await session.scalars(select(Category))
|
||||||
|
return categories
|
||||||
|
|
||||||
|
|
||||||
|
async def get_items_by_category(category_id: int):
|
||||||
|
async with async_session() as session:
|
||||||
|
items = await session.scalars(select(Item).where(Item.category == category_id))
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
async def get_item_by_id(item_id: int):
|
||||||
|
async with async_session() as session:
|
||||||
|
item = await session.scalar(select(Item).where(Item.id == item_id))
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_basket(tg_id, item_id):
|
||||||
|
async with async_session() as session:
|
||||||
|
user = await session.scalar(select(User).where(User.tg_id == tg_id))
|
||||||
|
await session.execute(delete(Basket).where(Basket.user == user.id, Basket.item == item_id))
|
||||||
|
await session.commit()
|
||||||
72
bot-market/app/handlers.py
Normal file
72
bot-market/app/handlers.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
from aiogram import Router, F
|
||||||
|
from aiogram.types import Message, CallbackQuery
|
||||||
|
from aiogram.filters import CommandStart, Command
|
||||||
|
|
||||||
|
import app.keyboards as kb
|
||||||
|
from app.database.requests import (get_item_by_id, set_user,
|
||||||
|
set_basket, get_basket, get_item_by_id, delete_basket)
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(CommandStart())
|
||||||
|
@router.callback_query(F.data == 'to_main')
|
||||||
|
async def cmd_start(message: Message | CallbackQuery):
|
||||||
|
if isinstance(message, Message):
|
||||||
|
await set_user(message.from_user.id)
|
||||||
|
await message.answer("Добро пожаловать в интернет магазин!",
|
||||||
|
reply_markup=kb.main)
|
||||||
|
else:
|
||||||
|
await message.answer('Вы вернулись на главную')
|
||||||
|
await message.message.answer("Добро пожаловать в интернет магазин!",
|
||||||
|
reply_markup=kb.main)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'catalog')
|
||||||
|
async def catalog(callback: CallbackQuery):
|
||||||
|
await callback.answer('')
|
||||||
|
await callback.message.edit_text(text='Выберите категорию.',
|
||||||
|
reply_markup=await kb.categories())
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('category_'))
|
||||||
|
async def category(callback: CallbackQuery):
|
||||||
|
await callback.answer('')
|
||||||
|
await callback.message.edit_text('Выберите товар',
|
||||||
|
reply_markup=await kb.items(callback.data.split('_')[1]))
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('item_'))
|
||||||
|
async def category(callback: CallbackQuery):
|
||||||
|
item = await get_item_by_id(callback.data.split('_')[1])
|
||||||
|
await callback.answer('')
|
||||||
|
await callback.message.answer_photo(photo=item.photo,
|
||||||
|
caption=f'{item.name}\n\n{item.description}\n\nЦена: {item.price} рублей',
|
||||||
|
reply_markup=await kb.basket(item.id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('order_'))
|
||||||
|
async def basket(callback: CallbackQuery):
|
||||||
|
await set_basket(callback.from_user.id, callback.data.split('_')[1])
|
||||||
|
await callback.answer('Товар добавлен в корзину')
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == 'mybasket')
|
||||||
|
async def mybasket(callback: CallbackQuery):
|
||||||
|
await callback.answer('')
|
||||||
|
basket = await get_basket(callback.from_user.id)
|
||||||
|
counter = 0
|
||||||
|
for item_info in basket:
|
||||||
|
item = await get_item_by_id(item_info.item)
|
||||||
|
await callback.message.answer_photo(photo=item.photo,
|
||||||
|
caption=f'{item.name}\n\n{item.description}\n\nЦена: {item.price} рублей',
|
||||||
|
reply_markup=await kb.delete_from_basket(item.id))
|
||||||
|
counter += 1
|
||||||
|
await callback.message.answer('Ваша корзина пуста') if counter == 0 else await callback.answer('')
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith('delete_'))
|
||||||
|
async def delete_from_basket(callback: CallbackQuery):
|
||||||
|
await delete_basket(callback.from_user.id, callback.data.split('_')[1])
|
||||||
|
await callback.message.delete()
|
||||||
|
await callback.answer('Вы удалили товар из корзины')
|
||||||
49
bot-market/app/keyboards.py
Normal file
49
bot-market/app/keyboards.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
from aiogram.types import (ReplyKeyboardMarkup, KeyboardButton,
|
||||||
|
InlineKeyboardMarkup, InlineKeyboardButton)
|
||||||
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
|
|
||||||
|
from app.database.requests import get_categories, get_items_by_category
|
||||||
|
|
||||||
|
main = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text='Каталог', callback_data='catalog')],
|
||||||
|
[InlineKeyboardButton(text='Корзина', callback_data='mybasket'),
|
||||||
|
InlineKeyboardButton(text='Контакты', callback_data='contacts')]
|
||||||
|
])
|
||||||
|
|
||||||
|
to_main = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text='На главную', callback_data='to_main')]
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_from_basket(order_id):
|
||||||
|
keyboard = InlineKeyboardBuilder()
|
||||||
|
keyboard.add(InlineKeyboardButton(text='Удалить из корзины', callback_data=f'delete_{order_id}'))
|
||||||
|
return keyboard.adjust(2).as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
async def basket(order_id):
|
||||||
|
keyboard = InlineKeyboardBuilder()
|
||||||
|
keyboard.add(InlineKeyboardButton(text='Оформить заказ', callback_data=f'order_{order_id}'))
|
||||||
|
keyboard.add(InlineKeyboardButton(text='Назад', callback_data='to_main'))
|
||||||
|
return keyboard.adjust(2).as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
async def categories():
|
||||||
|
all_categories = await get_categories()
|
||||||
|
keyboard = InlineKeyboardBuilder()
|
||||||
|
for category in all_categories:
|
||||||
|
keyboard.add(InlineKeyboardButton(text=category.name,
|
||||||
|
callback_data=f'category_{category.id}'))
|
||||||
|
keyboard.add(InlineKeyboardButton(text='Назад', callback_data='to_main'))
|
||||||
|
return keyboard.adjust(2).as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
async def items(category_id: int):
|
||||||
|
items = await get_items_by_category(category_id)
|
||||||
|
keyboard = InlineKeyboardBuilder()
|
||||||
|
for item in items:
|
||||||
|
keyboard.add(InlineKeyboardButton(text=item.name,
|
||||||
|
callback_data=f"item_{item.id}"))
|
||||||
|
keyboard.add(InlineKeyboardButton(text='Назад', callback_data='to_main'))
|
||||||
|
return keyboard.adjust(2).as_markup()
|
||||||
3
bot-market/config.py
Normal file
3
bot-market/config.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
TOKEN='7103505936:AAEpiQxlKNd9Uji9IziTdNzSTH38PavqXZM'
|
||||||
|
ENGINE='sqlite+aiosqlite:///db.sqlite3'
|
||||||
|
ECHO=''
|
||||||
25
bot-market/run.py
Normal file
25
bot-market/run.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiogram import Bot, Dispatcher
|
||||||
|
|
||||||
|
from config import TOKEN
|
||||||
|
from app.database.models import async_main
|
||||||
|
from app.handlers import router
|
||||||
|
from app.admin import admin
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await async_main()
|
||||||
|
bot = Bot(token=TOKEN)
|
||||||
|
dp = Dispatcher()
|
||||||
|
dp.include_routers(admin, router)
|
||||||
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
try:
|
||||||
|
asyncio.run(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('Exit')
|
||||||
BIN
bot_data.db
Normal file
BIN
bot_data.db
Normal file
Binary file not shown.
BIN
old/bot_klining.db
Normal file
BIN
old/bot_klining.db
Normal file
Binary file not shown.
Reference in New Issue
Block a user