commit 64c6aeb002b166f428d92fdf7ba70c53f8c983dd Author: tema Date: Sat Jun 4 22:02:51 2022 +0300 Init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1495ffe --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Bot files +*.sqlite3 +*.bak +*.env +!dist.env +venv/ +test.py +departed \ No newline at end of file diff --git a/bot.py b/bot.py new file mode 100755 index 0000000..5e3e98f --- /dev/null +++ b/bot.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import logging + +from aiogram import executor + +from load import dp, bot +import config +import filters +import handlers + + +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO +) + +logger = logging.getLogger(__name__) + + +async def on_startup(dp): + await bot.set_webhook(url=config.webhook_url) + + +async def on_shutdown(dp): + await bot.delete_webhook() + + +def main() -> None: + if config.use_webhook: + executor.start_webhook( + dispatcher=dp, + webhook_path=config.webhook_path, + on_startup=on_startup, + on_shutdown=on_shutdown, + skip_updates=config.skip_updates, + host=config.webhook_app_host, + port=config.webhook_app_port, + ) + else: + executor.start_polling(dp, skip_updates=config.skip_updates) + + +if __name__ == '__main__': + main() diff --git a/config.py b/config.py new file mode 100644 index 0000000..fe3c714 --- /dev/null +++ b/config.py @@ -0,0 +1,25 @@ +from environs import Env + + + +CONFIG_FILE = '.env' + +env = Env() +env.read_env(CONFIG_FILE) + +token = env.str("token") +db_url = env.str("database_url", "sqlite:///database.sqlite3") +permission_file = env.str("permission_file", "permission.json") + +use_webhook = env.bool("use_webhook", False) +skip_updates = env.bool("skip_update", True) +telegram_api_server = env.str("telegram_api_server", "https://api.telegram.org") + +if use_webhook: + webhook_app_host = env.str("app_host") + webhook_app_port = env.int("app_port") + + webhook_host = env.str("webhook_host") + + webhook_path = f"/bot{token}" + webhook_url = f"{webhook_host}{webhook_path}" diff --git a/dist.env b/dist.env new file mode 100644 index 0000000..826b807 --- /dev/null +++ b/dist.env @@ -0,0 +1,16 @@ +# Установите # в начало строки, чтобы использовать значение по умолчанию +# Строки которые имеют значение по умолчанию позначены # в конце строки + +token = "123:adminadmin" + +skip_update = False # +use_webhook = True # +#telegram_api_server = "" # +#permission_file = "" + +app_host = "127.0.0.1" +app_port = 2001 +webhook_host = "http://127.0.0.1:2001" + + +#database_url = "" # diff --git a/filters/__init__.py b/filters/__init__.py new file mode 100644 index 0000000..5c81432 --- /dev/null +++ b/filters/__init__.py @@ -0,0 +1,6 @@ +from load import dp + +from .admin import IsAdmin + + +dp.filters_factory.bind(IsAdmin) diff --git a/filters/admin.py b/filters/admin.py new file mode 100644 index 0000000..9a81827 --- /dev/null +++ b/filters/admin.py @@ -0,0 +1,14 @@ +from aiogram.types import Message +from aiogram.dispatcher.filters import BoundFilter + +from utils.database.base import get_admins + + +class IsAdmin(BoundFilter): + key = 'is_admin' + + def __init__(self, is_admin): + self.is_admin = is_admin + + async def check(self, message: Message): + return message.from_user.id in get_admins() \ No newline at end of file diff --git a/handlers/__init__.py b/handlers/__init__.py new file mode 100644 index 0000000..a5f3cc6 --- /dev/null +++ b/handlers/__init__.py @@ -0,0 +1,3 @@ +from . import user +from . import admin +from . import callback \ No newline at end of file diff --git a/handlers/admin/__init__.py b/handlers/admin/__init__.py new file mode 100644 index 0000000..1ceeb99 --- /dev/null +++ b/handlers/admin/__init__.py @@ -0,0 +1,4 @@ +from . import panel +from . import add_post +from . import delete_post +from . import user_control diff --git a/handlers/admin/add_post.py b/handlers/admin/add_post.py new file mode 100644 index 0000000..a281ec6 --- /dev/null +++ b/handlers/admin/add_post.py @@ -0,0 +1,85 @@ +import io + +from aiogram import types +from aiogram.types import ContentType +from aiogram.dispatcher import FSMContext + +from load import bot, dp, messages +from utils.helper import download_file +from keyboard.default.admin.main_menu import base_menu, continue_btn, all_right +from keyboard.default.main_menu import back_to_main_menu +from state.post import Post +from utils.database.market import add_item + + +@dp.message_handler(lambda x: x.text == messages.add_post, is_admin=True) +async def cmd_add_post(message: types.Message): + await bot.send_message(message.chat.id, "Укажите имя товара", reply_markup=base_menu()) + await Post.name.set() + + +@dp.message_handler(is_admin=True, state=Post.name) +async def admin_form_name(message: types.Message, state: FSMContext): + await state.update_data(name=message.text) + await bot.send_message(message.chat.id, "Укажите описание товара", reply_markup=base_menu(skip=True)) + await Post.description.set() + + +@dp.message_handler(is_admin=True, state=Post.description) +async def admin_form_desk(message: types.Message, state: FSMContext): + if message.text == messages.skip_message: + await state.update_data(description="") + else: + await state.update_data(description=message.text) + await bot.send_message(message.chat.id, "Укажите цену", reply_markup=base_menu()) + await Post.price.set() + + +@dp.message_handler(is_admin=True, state=Post.price) +async def admin_form_price(message: types.message, state: FSMContext): + if not message.text.replace('.','').replace(',', '').isdigit(): + await Post.price.set() + await state.update_data(price=float(message.text.replace(",", "."))) + await bot.send_message(message.chat.id, "Отправьте фото товара") + await Post.image.set() + + +@dp.message_handler(is_admin=True, state=Post.image, content_types=[ContentType.PHOTO]) +async def admin_form_image(message:types.Message, state: FSMContext): + file_id = message.photo[-1].file_id + file_info = await bot.get_file(file_id) + photo = await download_file(file_info.file_path) + await state.update_data(image=photo.read()) + await bot.send_message( + message.chat.id, + "Все данные для поста были заполнены", + reply_markup=continue_btn + ) + await Post.finish.set() + + +@dp.message_handler(lambda x:x.text == messages.continue_, state=Post.finish, is_admin=True) +async def finish(message: types.Message, state: FSMContext): + data = await state.get_data() + await bot.send_photo( + message.chat.id, + caption=messages.product_message.format( + name=data["name"], + description=data["description"], + price=data["price"] + ), + photo=data["image"], + reply_markup=all_right, + parse_mode='Markdown' + ) + +@dp.message_handler(lambda x: x.text == messages.all_right_message, is_admin=True, state=Post.finish) +async def write_on_db(message: types.Message, state: FSMContext): + data = await state.get_data() + add_item(**data) + await state.finish() + await bot.send_message( + message.chat.id, + "Товар был добавлен", + reply_markup=back_to_main_menu + ) diff --git a/handlers/admin/delete_post.py b/handlers/admin/delete_post.py new file mode 100644 index 0000000..a1e0b86 --- /dev/null +++ b/handlers/admin/delete_post.py @@ -0,0 +1,27 @@ +from aiogram import types + +from load import bot, dp, messages +from utils.database.market import Catalog +from keyboard.default.main_menu import back_to_main_menu +from keyboard.inline.admin.catalog import item_list + + +@dp.message_handler(lambda x: x.text == messages.delete_post, is_admin=True) +async def delete_post(message: types.Message): + items = Catalog.get_catalog() + if not items: + await bot.send_message(message.chat.id, "Каталог пуст:(") + return + item = items[0] + await bot.send_message(message.chat.id, messages.catalog, reply_markup=back_to_main_menu) + await bot.send_photo( + chat_id=message.chat.id, + photo=item["image"], + caption=messages.product_message.format( + name=item["name"], + description=item["description"], + price=item['price'] + ), + parse_mode="Markdown", + reply_markup=item_list(items=len(items)-1) + ) diff --git a/handlers/admin/panel.py b/handlers/admin/panel.py new file mode 100644 index 0000000..22d76c4 --- /dev/null +++ b/handlers/admin/panel.py @@ -0,0 +1,10 @@ +from aiogram import types + +from load import dp, bot, messages +from keyboard.default.admin.main_menu import main_menu + + +@dp.message_handler(lambda x: x.text == messages.admin_panel, is_admin=True) +async def cmd_menu(message: types.Message): + await bot.send_message(message.chat.id, "Admin panel", reply_markup=main_menu()) + diff --git a/handlers/admin/user_control/__init__.py b/handlers/admin/user_control/__init__.py new file mode 100644 index 0000000..4ebb87f --- /dev/null +++ b/handlers/admin/user_control/__init__.py @@ -0,0 +1,6 @@ +from utils.json import permission + +if permission.can_admin_add_admins: + from . import adding +if permission.can_admin_del_admins: + from . import delete \ No newline at end of file diff --git a/handlers/admin/user_control/adding.py b/handlers/admin/user_control/adding.py new file mode 100644 index 0000000..4355158 --- /dev/null +++ b/handlers/admin/user_control/adding.py @@ -0,0 +1,37 @@ +from aiogram import types + +from load import dp, bot, messages +from state.state import AddUser +from utils.database.user import Register, User +from keyboard.default.admin.main_menu import base_menu +from keyboard.default.main_menu import back_to_main_menu + + + +@dp.message_handler(lambda x: x.text == messages.add_admin, is_admin=True) +async def add_admin(message: types.Message): + await bot.send_message( + message.chat.id, + ("Отправьте user_id пользователя\n" + "Его можно узнать зайдя в информацию о пользователе"), + reply_markup=base_menu() + ) + await AddUser.user_id.set() + + +@dp.message_handler(state=AddUser.user_id) +async def add_admin1(message: types.Message, state): + await state.finish() + user_id = message.text + if not user_id.isdigit() or "-100" in user_id: + await bot.send_message(message.chat.id, "Данные не правильные!", reply_markup=back_to_main_menu) + return + + user_id = int(user_id) + user = User.get_user(user_id) + if not user: + await bot.send_message(message.chat.id, "Пользователь не существует!", reply_markup=back_to_main_menu) + return + Register.register_admin(user) + await bot.send_message(message.chat.id, "Администратор добавлен!", reply_markup=back_to_main_menu) + diff --git a/handlers/admin/user_control/delete.py b/handlers/admin/user_control/delete.py new file mode 100644 index 0000000..91183af --- /dev/null +++ b/handlers/admin/user_control/delete.py @@ -0,0 +1,17 @@ +from aiogram import types + +from load import dp, bot, messages +from keyboard.inline.admin.user import item_list +from utils.database.base import get_full_admin + +@dp.message_handler(lambda x: x.text==messages.del_admin, is_admin=True) +async def del_admin(message: types.Message): + admins = get_full_admin() + admin = admins[0] + result = messages.admin_user.format(**admin) + await bot.send_message( + message.chat.id, + result, + parse_mode="Markdown", + reply_markup=item_list(items=len(admins)-1) + ) diff --git a/handlers/callback/__init__.py b/handlers/callback/__init__.py new file mode 100644 index 0000000..25c6b69 --- /dev/null +++ b/handlers/callback/__init__.py @@ -0,0 +1,4 @@ +from . import null +from . import catalog +from . import cart +from . import admin \ No newline at end of file diff --git a/handlers/callback/admin/__init__.py b/handlers/callback/admin/__init__.py new file mode 100644 index 0000000..310efb9 --- /dev/null +++ b/handlers/callback/admin/__init__.py @@ -0,0 +1,2 @@ +from . import catalog +from . import user \ No newline at end of file diff --git a/handlers/callback/admin/catalog.py b/handlers/callback/admin/catalog.py new file mode 100644 index 0000000..3f566fb --- /dev/null +++ b/handlers/callback/admin/catalog.py @@ -0,0 +1,60 @@ +import io + +from aiogram import types + +from load import dp, messages +from utils.database.market import Catalog +from keyboard.inline.admin.catalog import item_list +from keyboard.default.main_menu import back_to_main_menu + + +@dp.callback_query_handler(lambda c: c.data.split("|")[0] in ["adm_prev", "adm_next"], is_admin=True) +async def next_item_adm(callback: types.CallbackQuery): + data = callback.data.split("|") + count = int(data[1]) + items = Catalog.get_catalog() + item = items[count] + + await callback.message.edit_media( + media=types.InputMediaPhoto( + media=io.BytesIO(item["image"]), + caption=messages.product_message.format( + name=item["name"], + description=item["description"], + price=item['price'] + ), + parse_mode="Markdown", + ), + reply_markup=item_list(count, len(items)-1, int(data[2])) + ) + +@dp.callback_query_handler(lambda c: c.data.split("|")[0] == 'delete_post', is_admin=True) +async def delete_post_adm(callback: types.CallbackQuery): + data = callback.data.split("|") + item_id = Catalog.get_catalog()[int(data[1])]["id"] + Catalog.delete_post(item_id) + + items = Catalog.get_catalog() + if items: + item = items[0] + await callback.message.edit_media( + media=types.InputMediaPhoto( + media=io.BytesIO(item["image"]), + caption=messages.product_message.format( + name=item["name"], + description=item["description"], + price=item['price'] + ), + parse_mode="Markdown", + ), + reply_markup=item_list(items=len(items)-1) + ) + else: + await callback.message.delete() + await dp.bot.send_message( + callback.message.chat.id, + messages.catalog + " пуст!", + reply_markup=back_to_main_menu + ) + await callback.answer("Товар удалён!") + await callback.answer() \ No newline at end of file diff --git a/handlers/callback/admin/user.py b/handlers/callback/admin/user.py new file mode 100644 index 0000000..2237721 --- /dev/null +++ b/handlers/callback/admin/user.py @@ -0,0 +1,35 @@ +from subprocess import call +from aiogram import types + +from load import dp, messages +from keyboard.inline.admin.user import item_list +from utils.database.base import get_full_admin, del_admin + + +@dp.callback_query_handler(lambda c: c.data.split("|")[0] in ["admin_prev", "admin_next"], is_admin=True) +async def next_item_adm(callback: types.CallbackQuery): + data = callback.data.split("|") + count = int(data[1]) + items = get_full_admin() + admin = items[count] + + await callback.message.edit_text( + messages.admin_user.format(**admin), + reply_markup=item_list(count, len(items)-1, int(data[2])), + parse_mode="Markdown" + ) + await callback.answer() + +@dp.callback_query_handler(lambda x: x.data.split("|")[0] == "delete_admin") +async def delete_admin(callback: types.CallbackQuery): + data = callback.data.split("|") + count = int(data[1]) + items = get_full_admin() + admin = items[count] + if admin["user_id"] == callback.from_user.id: + await dp.bot.send_message(callback.message.chat.id, "Вы не можете удалить сами себя") + await callback.answer() + return + del_admin(admin["user_id"]) + await dp.bot.send_message(callback.message.chat.id, f"Админ {admin['first_name']} удалён!") + await callback.answer() diff --git a/handlers/callback/cart.py b/handlers/callback/cart.py new file mode 100644 index 0000000..1cd74d3 --- /dev/null +++ b/handlers/callback/cart.py @@ -0,0 +1,74 @@ +import io + +from aiogram import types + +from load import dp, messages +from utils.database.cart import add_to_cart, get_user_cart, del_from_cart +from utils.database.market import Catalog +from keyboard.inline.cart import cart_list +from keyboard.default.main_menu import back_to_main_menu + + +@dp.callback_query_handler(lambda c: c.data.split("|")[0]=="add_to_cart") +async def add_cart(callback: types.CallbackQuery): + item_id = callback.data.split("|")[1] + add_to_cart(callback.from_user.id, item_id) + await callback.answer("Товар добавлен!") + + +@dp.callback_query_handler(lambda c: c.data.split("|")[0]=="del_from_cart") +async def del_cart(callback: types.CallbackQuery): + c = callback.data.split("|")[1] + item_id = get_user_cart(callback.from_user.id)[int(c)][0] + + del_from_cart(callback.from_user.id, item_id) + + items = get_user_cart(callback.from_user.id) + + if items: + index, count =items[0] + item = Catalog.get_catalog(index) + await callback.message.edit_media( + media=types.InputMediaPhoto( + media=io.BytesIO(item["image"]), + caption=messages.cart_message.format( + name=item["name"], + description=item["description"], + count=count, + price=item['price']*count + ), + parse_mode="Markdown", + ), + reply_markup=cart_list(items=len(items)-1) + ) + else: + await callback.message.delete() + await dp.bot.send_message( + callback.message.chat.id, + messages.cart + " пустая!", + reply_markup=back_to_main_menu + ) + await callback.answer("Товар удалён!") + + +@dp.callback_query_handler(lambda c: c.data.split("|")[0] in ["cart_prev", "cart_next"]) +async def next_item(callback: types.CallbackQuery): + data = callback.data.split("|") + c = int(data[1]) + items = get_user_cart(callback.from_user.id) + index, count = items[c] + item = Catalog.get_catalog(index) + + await callback.message.edit_media( + media=types.InputMediaPhoto( + media=io.BytesIO(item["image"]), + caption=messages.cart_message.format( + name=item["name"], + description=item["description"], + count=count, + price=item['price']*count + ), + parse_mode="Markdown", + ), + reply_markup=cart_list(c, len(items)-1) + ) diff --git a/handlers/callback/catalog.py b/handlers/callback/catalog.py new file mode 100644 index 0000000..6225cc5 --- /dev/null +++ b/handlers/callback/catalog.py @@ -0,0 +1,29 @@ +import io + +from aiogram import types + +from load import dp, messages +from utils.database.market import Catalog +from keyboard.inline.catalog import item_list + + +@dp.callback_query_handler(lambda c: c.data.split("|")[0] in ["prev", "next"]) +async def next_item(callback: types.CallbackQuery): + data = callback.data.split("|") + count = int(data[1]) + items = Catalog.get_catalog() + item = items[count] + + await callback.message.edit_media( + media=types.InputMediaPhoto( + media=io.BytesIO(item["image"]), + caption=messages.product_message.format( + name=item["name"], + description=item["description"], + price=item['price'] + ), + parse_mode="Markdown", + ), + reply_markup=item_list(count, len(items)-1, int(data[2])) + ) + diff --git a/handlers/callback/null.py b/handlers/callback/null.py new file mode 100644 index 0000000..d076309 --- /dev/null +++ b/handlers/callback/null.py @@ -0,0 +1,7 @@ +from aiogram.types import CallbackQuery + +from load import dp + +@dp.callback_query_handler(lambda c: c.data == "null") +async def null(callback: CallbackQuery): + await callback.answer() diff --git a/handlers/user/__init__.py b/handlers/user/__init__.py new file mode 100644 index 0000000..2022a1a --- /dev/null +++ b/handlers/user/__init__.py @@ -0,0 +1,5 @@ +from . import start +from . import catalog +from . import cart +from . import checkout +from . import ordering diff --git a/handlers/user/cart.py b/handlers/user/cart.py new file mode 100644 index 0000000..dfe8bf3 --- /dev/null +++ b/handlers/user/cart.py @@ -0,0 +1,40 @@ +from aiogram import types + +from load import dp, bot, messages +from keyboard.default.main_menu import cart_btn +from keyboard.inline.cart import cart_list +from utils.database.cart import get_user_cart, clean_cart +from utils.database.market import Catalog + + +@dp.message_handler(lambda x: x.text == messages.cart) +async def cmd_cart(message: types.Message): + items = get_user_cart(message.from_user.id) + + if not items: + await bot.send_message(message.chat.id, messages.cart + " пустая!", reply_markup=cart_btn()) + return + + index, count = items[0] + item = Catalog.get_catalog(index) + + await bot.send_message(message.chat.id, messages.cart, reply_markup=cart_btn(False)) + + await bot.send_photo( + chat_id=message.chat.id, + photo=item["image"], + caption=messages.cart_message.format( + name=item["name"], + description=item["description"], + count=count, + price=item['price']*count + ), + parse_mode="Markdown", + reply_markup=cart_list(items=len(items)-1) + ) + + +@dp.message_handler(lambda x: x.text == messages.clean_cart) +async def cmd_clean_cart(message: types.Message): + clean_cart(message.from_user.id) + await bot.send_message(message.chat.id, messages.cart + " очищена", reply_markup=cart_btn()) diff --git a/handlers/user/catalog.py b/handlers/user/catalog.py new file mode 100644 index 0000000..95314ee --- /dev/null +++ b/handlers/user/catalog.py @@ -0,0 +1,27 @@ +from aiogram import types + +from load import dp, bot, messages +from utils.database.market import Catalog +from keyboard.inline.catalog import item_list +from keyboard.default.main_menu import back_to_main_menu + + +@dp.message_handler(lambda x: x.text == messages.catalog) +async def catalog(message: types.Message): + items = Catalog.get_catalog() + if not items: + await bot.send_message(message.chat.id, "Каталог пуст:(") + return + item = items[0] + await bot.send_message(message.chat.id, messages.catalog, reply_markup=back_to_main_menu) + await bot.send_photo( + chat_id=message.chat.id, + photo=item["image"], + caption=messages.product_message.format( + name=item["name"], + description=item["description"], + price=item['price'] + ), + parse_mode="Markdown", + reply_markup=item_list(items=len(items)) + ) \ No newline at end of file diff --git a/handlers/user/checkout.py b/handlers/user/checkout.py new file mode 100644 index 0000000..1c97ba5 --- /dev/null +++ b/handlers/user/checkout.py @@ -0,0 +1,35 @@ +from aiogram import types + +from load import dp, bot, messages +from utils.database.cart import get_user_cart +from utils.database.market import Catalog +from utils.database.ordering import save_info +from keyboard.default.checkout import checkout_btn +from state.state import UserState + + +@dp.message_handler(lambda x: x.text == messages.checkout) +async def checkout(message:types.Message): + items = get_user_cart(message.from_user.id) + output: str = "" + cost = 0 + for index, count in items: + i = Catalog.get_catalog(index) + output += f'Имя: {i["name"]} цена: {i["price"]} - {count}шт.\n' + cost += i["price"] * count + output += f"Общая сумма: {cost}\nВсё верно? Следующий этап: заполнение заявки" + await bot.send_message(message.chat.id, output, reply_markup=checkout_btn()) + await UserState.confirm.set() + + +@dp.message_handler(lambda x: x.text == messages.all_right_message) +async def continue_user_form(message: types.Message): + pass + + +@dp.message_handler(lambda x: x.text == messages.save_and_continue, state="*") +async def save_user_info(message: types.Message): + state = dp.current_state(chat=message.chat.id, user=message.from_user.id) + info = await state.get_data() + save_info(user_id=message.from_user.id, **info) + await continue_user_form(message) diff --git a/handlers/user/ordering.py b/handlers/user/ordering.py new file mode 100644 index 0000000..a7ddc1c --- /dev/null +++ b/handlers/user/ordering.py @@ -0,0 +1,108 @@ +from aiogram import types +from aiogram.dispatcher import FSMContext + +from load import dp, bot, messages +from keyboard.default.checkout import confirm_all_info +from keyboard.default.main_menu import cancel_btn, get_phone_number, continue_btn +from keyboard.default.ordering import load_info +from state.state import UserState +from state.checkout import Checkout +from utils.database.ordering import get_info + + +@dp.message_handler(lambda x: x.text == messages.all_right_message, state=UserState.confirm) +async def confirm(message: types.Message, state: FSMContext): + await state.finish() + info = get_info(message.from_user.id) + + await bot.send_message( + message.chat.id, + "Может у вас есть сохраненные данные?", + reply_markup=load_info(bool(info)) + ) + await Checkout.loaded.set() + + +@dp.message_handler(lambda x: x.text == messages.load_data, state=Checkout.loaded) +async def load_form(message: types.message, state: FSMContext): + info = get_info(message.from_user.id) + await state.update_data( + loaded=True, + first_name = info["first_name"], + last_name = info["last_name"], + phone_number = info["phone_number"], + address = info["address"] + ) + await bot.send_message(message.chat.id, "Ваши данные загружены!", reply_markup=continue_btn) + await Checkout.finish.set() + + +@dp.message_handler(lambda x: x.text == messages.rewrite_data, state=Checkout.loaded) +async def new_form(message: types.Message, state: FSMContext = None): + await state.update_data(loaded=False) + await bot.send_message( + message.chat.id, + "Напишите вашу фамилию", + reply_markup=cancel_btn + ) + await Checkout.last_name.set() + + +@dp.message_handler(state=Checkout.last_name) +async def form_last_name(message: types.Message, state: FSMContext): + await state.update_data(last_name=message.text) + await bot.send_message( + message.chat.id, + "Ваше имя", + reply_markup=cancel_btn + ) + await Checkout.first_name.set() + + +@dp.message_handler(state=Checkout.first_name) +async def form_first_name(message: types.Message, state: FSMContext): + await state.update_data(first_name=message.text) + await bot.send_message( + message.chat.id, + ("Номер телефона\n\n" + "Можете написать номер телефона вручную"), + reply_markup=get_phone_number + ) + await Checkout.phone_number.set() + + +@dp.message_handler(state=Checkout.phone_number, content_types=[types.ContentType.CONTACT, types.ContentType.TEXT]) +async def form_phone(message: types.Message, state: FSMContext): + if message.contact is not None: + phone=message.contact.phone_number + else: phone=message.text + if len(phone) in [10,12,13]: + await state.update_data(phone_number=phone) + await bot.send_message(message.chat.id, "Отправте адрес доставки", reply_markup=cancel_btn) + await Checkout.address.set() + else: + await bot.send_message(message.chat.id, "Номер телефона не правильный!") + await Checkout.phone_number.set() + + +@dp.message_handler(state=Checkout.address) +async def form_address(message: types.Message, state: FSMContext): + await state.update_data(address=message.text) + await bot.send_message(message.chat.id, "Отлично заявка заполнена!", reply_markup=continue_btn) + await Checkout.finish.set() + + +@dp.message_handler(state=Checkout.finish) +async def finish_form(message: types.Message, state: FSMContext): + data = await state.get_data() + + text = (f"Имя: {data['first_name']}\n" + f"Фамилия: {data['last_name']}\n" + f"Номер телефона: {data['phone_number']}\n" + f"Адрес: {data['address']}\n" + ) + await bot.send_message( + message.chat.id, + text, + reply_markup=confirm_all_info() + ) diff --git a/handlers/user/start.py b/handlers/user/start.py new file mode 100644 index 0000000..e10ecaa --- /dev/null +++ b/handlers/user/start.py @@ -0,0 +1,50 @@ +from aiogram import types +from aiogram.dispatcher.filters import CommandStart + +from load import dp, bot, messages +from keyboard.default.main_menu import main_menu +from utils.database.base import get_admins, get_operator +from utils.database.user import Register + + +@dp.message_handler(CommandStart()) +async def start(message: types.Message): + Register.register_user(message.from_user) + is_bot_admin = message.from_user.id in get_admins() + is_operator = message.from_user.id in get_operator() + await bot.send_message( + message.chat.id, + messages.welcome.format(user=message.from_user.first_name), + reply_markup=main_menu(admin=is_bot_admin, operator=is_operator) + ) + +@dp.message_handler(lambda x: x.text == messages.back) +async def back_to_menu(message: types.Message): + is_bot_admin = message.from_user.id in get_admins() + is_operator = message.from_user.id in get_operator() + await bot.send_message( + message.chat.id, + "Главное меню", + reply_markup=main_menu(admin=is_bot_admin, operator=is_operator) + ) + + +@dp.message_handler(lambda x: x.text == messages.cancel_message, state="*") +async def cancel(message: types.Message): + state = dp.current_state(chat=message.chat.id, user=message.from_user.id) + await state.finish() + await back_to_menu(message) + + +@dp.message_handler(lambda x:x.text == messages.info) +async def userinfo(message: types.Message): + await bot.send_message( + message.chat.id, + ("User_id: {user_id}").format(user_id=message.from_user.id), + parse_mode="HTML" + ) + + +@dp.message_handler(commands=['test']) +async def test(message: types.Message): + Register.register_admin(message.from_user) diff --git a/keyboard/__init__.py b/keyboard/__init__.py new file mode 100644 index 0000000..9dc2ba6 --- /dev/null +++ b/keyboard/__init__.py @@ -0,0 +1,2 @@ +from . import default +from . import inline \ No newline at end of file diff --git a/keyboard/default/__init__.py b/keyboard/default/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/keyboard/default/admin/__init__.py b/keyboard/default/admin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/keyboard/default/admin/main_menu.py b/keyboard/default/admin/main_menu.py new file mode 100644 index 0000000..1c9b80b --- /dev/null +++ b/keyboard/default/admin/main_menu.py @@ -0,0 +1,41 @@ +from aiogram.types.reply_keyboard import ReplyKeyboardMarkup, KeyboardButton + +from load import messages +from utils.json import permission + + +def main_menu(): + markup = ReplyKeyboardMarkup(resize_keyboard=True) + + markup.add(KeyboardButton(messages.add_post), KeyboardButton(messages.delete_post)) + admin_control = [] + if permission.can_admin_add_admins: + admin_control.append(KeyboardButton(messages.add_admin)) + if permission.can_admin_del_admins: + admin_control.append(KeyboardButton(messages.del_admin)) + if admin_control: + markup.add(*admin_control) + markup.add(KeyboardButton(messages.back)) + + return markup + + +def base_menu(cancel=True, skip=False): + markup = ReplyKeyboardMarkup(resize_keyboard=True) + if cancel: + markup.row(KeyboardButton(messages.cancel_message)) + if skip: + markup.row(KeyboardButton(messages.skip_message)) + return markup + + +continue_btn = ReplyKeyboardMarkup( + [[KeyboardButton(messages.continue_), + KeyboardButton(messages.cancel_message)]], + resize_keyboard=True +) +all_right = ReplyKeyboardMarkup( + [[KeyboardButton(messages.all_right_message), + KeyboardButton(messages.cancel_message)]], + resize_keyboard=True +) diff --git a/keyboard/default/checkout.py b/keyboard/default/checkout.py new file mode 100644 index 0000000..cf2df2e --- /dev/null +++ b/keyboard/default/checkout.py @@ -0,0 +1,16 @@ +from aiogram.types.reply_keyboard import ReplyKeyboardMarkup, KeyboardButton + +from load import messages + +def checkout_btn() -> ReplyKeyboardMarkup: + return ReplyKeyboardMarkup([[ + KeyboardButton(messages.all_right_message), + KeyboardButton(messages.cancel_message) + ]], resize_keyboard=True) + +def confirm_all_info(): + markup = ReplyKeyboardMarkup(resize_keyboard=True) + markup.add(KeyboardButton(messages.all_right_message)) + markup.row(KeyboardButton(messages.save_and_continue)) + markup.row(KeyboardButton(messages.cancel_message)) + return markup diff --git a/keyboard/default/main_menu.py b/keyboard/default/main_menu.py new file mode 100644 index 0000000..8fed28f --- /dev/null +++ b/keyboard/default/main_menu.py @@ -0,0 +1,41 @@ +from aiogram.types.reply_keyboard import ReplyKeyboardMarkup, KeyboardButton + +from load import messages + + +def main_menu(admin=False, operator=False): + markup = ReplyKeyboardMarkup( + [ + [ + KeyboardButton(messages.catalog), # Go to catalog + KeyboardButton(messages.cart) # Go to cart + ] + ], + resize_keyboard=True + ).add(KeyboardButton(messages.info)) + if admin == True: + markup.add(KeyboardButton(messages.admin_panel)) + + if operator == True: + markup.add(KeyboardButton(messages.operator_panel)) + + return markup + + +def cart_btn(is_empty=True): + markup = ReplyKeyboardMarkup(resize_keyboard=True) + if not is_empty: + markup.add(KeyboardButton(messages.checkout)) + markup.add(KeyboardButton(messages.clean_cart)) + markup.add(KeyboardButton(messages.back)) + return markup + +back_to_main_menu = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(messages.back)]],resize_keyboard=True) +cancel_btn = ReplyKeyboardMarkup([[KeyboardButton(messages.cancel_message)]], resize_keyboard=True) +get_phone_number = ReplyKeyboardMarkup( + keyboard=[ + [KeyboardButton(messages.contact, request_contact=True)] + ], + resize_keyboard=True +).row(KeyboardButton(messages.cancel_message)) +continue_btn = ReplyKeyboardMarkup([[KeyboardButton(messages.continue_)]], resize_keyboard=True) diff --git a/keyboard/default/ordering.py b/keyboard/default/ordering.py new file mode 100644 index 0000000..c586d1e --- /dev/null +++ b/keyboard/default/ordering.py @@ -0,0 +1,10 @@ +from aiogram.types.reply_keyboard import ReplyKeyboardMarkup, KeyboardButton + +from load import messages + +def load_info(data:bool = False): + markup = ReplyKeyboardMarkup(resize_keyboard=True) + if data: + markup.row(KeyboardButton(messages.load_data)) + markup.row(KeyboardButton(messages.rewrite_data)) + return markup diff --git a/keyboard/inline/__init__.py b/keyboard/inline/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/keyboard/inline/admin/catalog.py b/keyboard/inline/admin/catalog.py new file mode 100644 index 0000000..edd24d9 --- /dev/null +++ b/keyboard/inline/admin/catalog.py @@ -0,0 +1,26 @@ +from aiogram.types.inline_keyboard import InlineKeyboardMarkup, InlineKeyboardButton + +from load import messages + + +def item_list(item=0, items:int=1, user_count:int=1) -> InlineKeyboardMarkup: + markup = InlineKeyboardMarkup() + default = [ + InlineKeyboardButton(user_count, callback_data='null') + ] + back = ['⬅️', f"adm_prev|{item-1}|{user_count-1}"] + next = ['➡️', f"adm_next|{item+1}|{user_count+1}"] + + + if items < 1 or item >= items: + next = [" ", "null"] + + if item == 0: + back = [" ", "null"] + + + default.append(InlineKeyboardButton(next[0], callback_data=next[1])) + + markup.add(InlineKeyboardButton(back[0], callback_data=back[1]), *default) + markup.row(InlineKeyboardButton(messages.delete_post, callback_data=f"delete_post|{item}")) + return markup diff --git a/keyboard/inline/admin/user.py b/keyboard/inline/admin/user.py new file mode 100644 index 0000000..fd1724d --- /dev/null +++ b/keyboard/inline/admin/user.py @@ -0,0 +1,24 @@ +from aiogram.types.inline_keyboard import InlineKeyboardButton, InlineKeyboardMarkup +from load import messages + +def item_list(item=0, items:int=1, user_count:int=1) -> InlineKeyboardMarkup: + markup = InlineKeyboardMarkup() + default = [ + InlineKeyboardButton(user_count, callback_data='null') + ] + back = ['⬅️', f"admin_prev|{item-1}|{user_count-1}"] + next = ['➡️', f"admin_next|{item+1}|{user_count+1}"] + + + if items < 1 or item >= items: + next = [" ", "null"] + + if item == 0: + back = [" ", "null"] + + + default.append(InlineKeyboardButton(next[0], callback_data=next[1])) + + markup.add(InlineKeyboardButton(back[0], callback_data=back[1]), *default) + markup.row(InlineKeyboardButton(messages.del_admin, callback_data=f"delete_admin|{item}")) + return markup \ No newline at end of file diff --git a/keyboard/inline/cart.py b/keyboard/inline/cart.py new file mode 100644 index 0000000..c384011 --- /dev/null +++ b/keyboard/inline/cart.py @@ -0,0 +1,24 @@ +from aiogram.types.inline_keyboard import InlineKeyboardMarkup, InlineKeyboardButton + +from load import messages + +def cart_list(item=0, items:int=1) -> InlineKeyboardMarkup: + markup = InlineKeyboardMarkup() + default = [ + InlineKeyboardButton(item+1, callback_data='null') + ] + back = ['⬅️', f"cart_prev|{item-1}"] + next = ['➡️', f"cart_next|{item+1}"] + + + if items < 1 or item >= items: + next = [" ", "null"] + + if item <= 0: + back = [" ", "null"] + + default.append(InlineKeyboardButton(next[0], callback_data=next[1])) + + markup.add(InlineKeyboardButton(back[0], callback_data=back[1]), *default) + markup.row(InlineKeyboardButton(messages.delete, callback_data=f"del_from_cart|{item}")) + return markup diff --git a/keyboard/inline/catalog.py b/keyboard/inline/catalog.py new file mode 100644 index 0000000..2f62ea9 --- /dev/null +++ b/keyboard/inline/catalog.py @@ -0,0 +1,25 @@ +from aiogram.types.inline_keyboard import InlineKeyboardMarkup, InlineKeyboardButton + +from load import messages + + +def item_list(item=0, items:int=1, user_count:int=1) -> InlineKeyboardMarkup: + markup = InlineKeyboardMarkup() + default = [ + InlineKeyboardButton(user_count, callback_data='null') + ] + back = ['⬅️', f"prev|{item-1}|{user_count-1}"] + next = ['➡️', f"next|{item+1}|{user_count+1}"] + + + if items < 1 or item >= items: + next = [" ", "null"] + + if item == 0: + back = [" ", "null"] + + default.append(InlineKeyboardButton(next[0], callback_data=next[1])) + + markup.add(InlineKeyboardButton(back[0], callback_data=back[1]), *default) + markup.row(InlineKeyboardButton(messages.add_to_cart, callback_data=f"add_to_cart|{item}")) + return markup \ No newline at end of file diff --git a/keyboard/inline/checkout.py b/keyboard/inline/checkout.py new file mode 100644 index 0000000..90bf338 --- /dev/null +++ b/keyboard/inline/checkout.py @@ -0,0 +1,22 @@ +from aiogram.types.inline_keyboard import InlineKeyboardMarkup, InlineKeyboardButton + + +def cart_list(item=0, items:int=1) -> InlineKeyboardMarkup: + markup = InlineKeyboardMarkup() + default = [ + InlineKeyboardButton(item+1, callback_data='null') + ] + back = ['⬅️', f"cart_prev|{item-1}"] + next = ['➡️', f"cart_next|{item+1}"] + + + if items < 1 or item >= items: + next = [" ", "null"] + + if item <= 0: + back = [" ", "null"] + + default.append(InlineKeyboardButton(next[0], callback_data=next[1])) + + markup.add(InlineKeyboardButton(back[0], callback_data=back[1]), *default) + return markup diff --git a/load.py b/load.py new file mode 100644 index 0000000..16e4957 --- /dev/null +++ b/load.py @@ -0,0 +1,29 @@ +import json + +from aiogram import Dispatcher, Bot +from aiogram.bot.api import TelegramAPIServer +from aiogram.contrib.fsm_storage.memory import MemoryStorage + +from peewee import SqliteDatabase +from playhouse.db_url import connect + +import config +from utils import messages # used, do not delete! + + +# Bot init +bot = Bot( + token=config.token, + server=TelegramAPIServer.from_base(config.telegram_api_server) +) + +storage = MemoryStorage() + +dp = Dispatcher( + bot=bot, + storage=storage +) + +db:SqliteDatabase = connect(config.db_url) + +permissions = json.load(open(config.permission_file)) diff --git a/permission.json b/permission.json new file mode 100644 index 0000000..a57e405 --- /dev/null +++ b/permission.json @@ -0,0 +1,4 @@ +{ + "can_admin_add_admins": true, + "can_admin_del_admins": true +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1a5299d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +environs==9.5.0 +aiogram==2.20 +peewee==3.14.10 +pydantic==1.9.1 \ No newline at end of file diff --git a/state/__init__.py b/state/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/state/checkout.py b/state/checkout.py new file mode 100644 index 0000000..3e05acb --- /dev/null +++ b/state/checkout.py @@ -0,0 +1,11 @@ +from aiogram.dispatcher.filters.state import StatesGroup, State + +class Checkout(StatesGroup): + first_name = State() + last_name = State() + phone_number = State() + address = State() + + loaded = State() + finish = State() + diff --git a/state/post.py b/state/post.py new file mode 100644 index 0000000..80de28d --- /dev/null +++ b/state/post.py @@ -0,0 +1,9 @@ +from aiogram.dispatcher.filters.state import StatesGroup, State + +class Post(StatesGroup): + name = State() + description = State() + price = State() + image = State() + + finish = State() diff --git a/state/state.py b/state/state.py new file mode 100644 index 0000000..f5adc22 --- /dev/null +++ b/state/state.py @@ -0,0 +1,8 @@ +from aiogram.dispatcher.filters.state import StatesGroup, State + +class UserState(StatesGroup): + confirm = State() + + +class AddUser(StatesGroup): + user_id = State() diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/database/base.py b/utils/database/base.py new file mode 100644 index 0000000..b28665f --- /dev/null +++ b/utils/database/base.py @@ -0,0 +1,30 @@ +from .model import Admin, Operator + + +def get_admins(): + users = [] + for i in Admin.select(): + users.append(i.user_id) + return users + + +def get_operator(): + users = [] + for i in Operator.select(): + users.append(i.user_id) + return users + +def get_full_admin(): + usr = [] + for i in Admin.select(): + usr.append({ + "user_id": i.user_id, + "first_name": i.first_name, + "last_name": i.last_name, + "username": i.username + }) + return usr + + +def del_admin(user_id:int): + Admin.delete().where(Admin.user_id == user_id).execute() diff --git a/utils/database/cart.py b/utils/database/cart.py new file mode 100644 index 0000000..ec46dcd --- /dev/null +++ b/utils/database/cart.py @@ -0,0 +1,23 @@ +from .model import Cart + + +def add_to_cart(user_id:int, item_id: int): + if Cart.select().where(Cart.user_id==user_id, Cart.product_id==item_id).exists(): + count = Cart.get(Cart.user_id==user_id, Cart.product_id==item_id).count + Cart.update(count=count+1).where(Cart.user_id==user_id, Cart.product_id==item_id).execute() + return + Cart.insert(user_id=user_id, product_id=item_id, count=1).execute() + + +def del_from_cart(user_id: int, item_id: int): + Cart.delete().where(Cart.user_id==user_id, Cart.product_id==item_id).execute() + + +def clean_cart(user_id: int): + Cart.delete().where(Cart.user_id==user_id).execute() + +def get_user_cart(user_id:int): + cart = [] + for i in Cart.select().where(Cart.user_id == user_id): + cart.append((i.product_id, i.count)) + return cart \ No newline at end of file diff --git a/utils/database/market.py b/utils/database/market.py new file mode 100644 index 0000000..eb568f3 --- /dev/null +++ b/utils/database/market.py @@ -0,0 +1,49 @@ +from ast import Mod +import typing as t + +from .model import Catalog as Model + + +class Catalog(): + @staticmethod + def __get_item(item_id: int) -> dict: + item = {} + if item_id is not None: + i = Model.get_or_none(Model.id == item_id) + if i is not None: + item.update( + { + "id": i.id, + "name": i.name, + "description": i.description, + "price": i.price, + "image": i.image + } + ) + return item + + @classmethod + def get_catalog(self, item_id:int = None, get_count:bool = False) -> t.Union[list, dict]: + if item_id: + if get_count: + return self.__get_item(item_id), Model.select().count() + return self.__get_item(item_id) + + items = [] + for i in Model.select(): + items.append(self.__get_item(i.id)) + return items + + + @staticmethod + def delete_post(item_id: int): + return Model.delete_by_id(item_id) + + +def add_item(name: str, description: str, price:t.Union[int, float], image:bytes, **kw): + Model.insert( + name=name, + description=description, + price=price, + image=image + ).execute() diff --git a/utils/database/model.py b/utils/database/model.py new file mode 100644 index 0000000..2129594 --- /dev/null +++ b/utils/database/model.py @@ -0,0 +1,48 @@ +from peewee import (Model, BigIntegerField, TextField, BlobField, + IntegerField, CharField, FloatField, ForeignKeyField) + +from load import db + + +class BaseModel(Model): + '''Base model. Abstract Class''' + class Meta: + database = db + + +class User(BaseModel): + user_id = BigIntegerField(null=False, unique=True) + first_name = CharField(null=False, max_length=64) + last_name = CharField(null=True, max_length=64) + username = CharField(null=True, max_length=32) + + +class Admin(User): + pass + + +class Operator(User): + pass + + +class Catalog(BaseModel): + name = TextField() + description = TextField() + image = BlobField() + price = FloatField() + + +class Cart(BaseModel): + user_id = BigIntegerField() + product_id = IntegerField() + count = IntegerField(default=1) + + +class UserInfo(BaseModel): + user_id = BigIntegerField(null=False, unique=True) + first_name = TextField() + last_name = TextField() + phone_number = CharField(15) + address = TextField() + +db.create_tables([Cart, Catalog, Operator, Admin, User, UserInfo]) diff --git a/utils/database/ordering.py b/utils/database/ordering.py new file mode 100644 index 0000000..1742d8a --- /dev/null +++ b/utils/database/ordering.py @@ -0,0 +1,23 @@ +from .model import UserInfo + + +def save_info(user_id: int, last_name:str, first_name:str, phone_number:str, address:str, **kw): + UserInfo.insert( + user_id=user_id, + first_name=first_name, + last_name=last_name, + phone_number=phone_number, + address=address + ).on_conflict_replace().execute() + +def get_info(user_id: int): + output = None + if UserInfo.select().where(UserInfo.user_id == user_id).exists(): + user = UserInfo.get(UserInfo.user_id == user_id) + output = { + "last_name": user.last_name, + "first_name": user.first_name, + "phone_number": user.phone_number, + "address": user.address + } + return output diff --git a/utils/database/user.py b/utils/database/user.py new file mode 100644 index 0000000..f0d7d90 --- /dev/null +++ b/utils/database/user.py @@ -0,0 +1,45 @@ +from aiogram.types import User as UserType +from peewee import Model + +from .model import User as UserModel +from .model import Admin, Operator +from utils import types + + +class Register: + + @classmethod + def __register_user(self, model: Model, user: UserType): + model.insert( + user_id=user.id, + first_name=user.first_name, + last_name=user.last_name, + username=user.username + ).on_conflict_replace().execute() + + @classmethod + def register_user(self, user: UserType): + return self.__register_user(UserModel, user) + + @classmethod + def register_admin(self, user: UserType): + return self.__register_user(Admin, user) + + @classmethod + def register_operator(self, user: UserType): + return self.__register_user(Operator, user) + + +class User: + + @staticmethod + def get_user(user_id: int): + if not UserModel.select().where(UserModel.user_id==user_id).exists(): + return None + data = UserModel.get(UserModel.user_id==user_id) + return types.User( + user_id=data.user_id, + first_name=data.first_name, + last_name=data.last_name, + username=data.username + ) diff --git a/utils/helper.py b/utils/helper.py new file mode 100644 index 0000000..b6eacfc --- /dev/null +++ b/utils/helper.py @@ -0,0 +1,13 @@ +import io +import typing as t + +from load import bot, config + +async def download_file(file_path, *args, **kw) -> t.Union[io.BytesIO, t.Any]: + if config.telegram_api_server == "https://api.telegram.org": + return await bot.download_file(file_path, *args, **kw) + else: + with open(file_path, 'rb') as f: + file = f.read() + f.close() + return io.BytesIO(file) diff --git a/utils/json/__init__.py b/utils/json/__init__.py new file mode 100644 index 0000000..5918338 --- /dev/null +++ b/utils/json/__init__.py @@ -0,0 +1,5 @@ +from load import permissions as __permissions + +from .model import Permission + +permission = Permission(**__permissions) \ No newline at end of file diff --git a/utils/json/model.py b/utils/json/model.py new file mode 100644 index 0000000..d6cdac8 --- /dev/null +++ b/utils/json/model.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + +class Permission(BaseModel): + can_admin_add_admins: bool + can_admin_del_admins: bool diff --git a/utils/messages.py b/utils/messages.py new file mode 100644 index 0000000..680b0b7 --- /dev/null +++ b/utils/messages.py @@ -0,0 +1,54 @@ +catalog = '🛍️ Каталог' +cart = '🛒 Корзина' +back = "👈 Главное меню" #Назад +checkout = "📦 Оформить заказ" +add_to_cart = "➕ Добавить в корзину" +delete = "🗑️ Удалить" +clean_cart = "🗑️ Очистить корзину" +info = "ℹ️ Информация" + +confirm_message = '✅ Подтвердить заказ' +all_right_message = '✅ Все верно' +cancel_message = '🚫 Отменить' +skip_message = '⏩ Пропустить' +contact = "☎️ Предоставить номер телефона" + +save_and_continue = "Сохранить данные и продолжить" +continue_ = "Продолжить" + +rewrite_data = "Ввести заново" +load_data = "Загрузить данные" + + +#Admins \ Moder +admin_panel = "Админ панель" +operator_panel = "Панель оператора" +add_post = "Добавить пост" +delete_post = "🗑️ Удалить пост" +add_admin = "Добавить админа" +del_admin = "Удалить админа" +admin_user = ( + 'Имя: [{first_name}](tg://user?id={user_id})\n' + 'Фамилия: [{last_name}](tg://user?id={user_id})\n' + 'Имя пользователя: @{username}\n' +) + + +#balance = '💰 Баланс' +#delivery_status = '🚚 Статус заказа' + +product_message = ( + "*{name}*\n\n" + "{description}\n\n\n" + "Цена: {price}" +) +cart_message = ( + "*{name}*\n\n" + "{description}\n\n\n" + "Количество: {count}\n" + "Цена: {price}" +) +welcome = ( + "Привет {user}\n" + "Добро пожаловать в магазинчик" +) \ No newline at end of file diff --git a/utils/types.py b/utils/types.py new file mode 100644 index 0000000..901ec4d --- /dev/null +++ b/utils/types.py @@ -0,0 +1,23 @@ +import typing as t +from abc import ABC + + +class BaseUserFileds(ABC): + id: int + first_name: str + last_name: t.Union[str, None] = None + username: t.Union[str, None] = None + + +class User(BaseUserFileds): + + def __new__(self, **kw): + if "user_id" in kw: + self.id = kw["user_id"] + if "first_name" in kw: + self.first_name = kw["first_name"] + if "last_name" in kw: + self.last_name = kw["last_name"] + if "username" in kw: + self.username = kw["username"] + return self