Init commit

This commit is contained in:
tema 2022-06-04 22:02:51 +03:00
commit 64c6aeb002
Signed by: tema
GPG Key ID: 21FDB6D162488F6F
59 changed files with 1399 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -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

43
bot.py Executable file
View File

@ -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()

25
config.py Normal file
View File

@ -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}"

16
dist.env Normal file
View File

@ -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 = "" #

6
filters/__init__.py Normal file
View File

@ -0,0 +1,6 @@
from load import dp
from .admin import IsAdmin
dp.filters_factory.bind(IsAdmin)

14
filters/admin.py Normal file
View File

@ -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()

3
handlers/__init__.py Normal file
View File

@ -0,0 +1,3 @@
from . import user
from . import admin
from . import callback

View File

@ -0,0 +1,4 @@
from . import panel
from . import add_post
from . import delete_post
from . import user_control

View File

@ -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
)

View File

@ -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)
)

10
handlers/admin/panel.py Normal file
View File

@ -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())

View File

@ -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

View File

@ -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)

View File

@ -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)
)

View File

@ -0,0 +1,4 @@
from . import null
from . import catalog
from . import cart
from . import admin

View File

@ -0,0 +1,2 @@
from . import catalog
from . import user

View File

@ -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()

View File

@ -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()

74
handlers/callback/cart.py Normal file
View File

@ -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)
)

View File

@ -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]))
)

View File

@ -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()

View File

@ -0,0 +1,5 @@
from . import start
from . import catalog
from . import cart
from . import checkout
from . import ordering

40
handlers/user/cart.py Normal file
View File

@ -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())

27
handlers/user/catalog.py Normal file
View File

@ -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))
)

35
handlers/user/checkout.py Normal file
View File

@ -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)

108
handlers/user/ordering.py Normal file
View File

@ -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()
)

50
handlers/user/start.py Normal file
View File

@ -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: <code>{user_id}</code>").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)

2
keyboard/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from . import default
from . import inline

View File

View File

View File

@ -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
)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

View File

@ -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

View File

@ -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

24
keyboard/inline/cart.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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

29
load.py Normal file
View File

@ -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))

4
permission.json Normal file
View File

@ -0,0 +1,4 @@
{
"can_admin_add_admins": true,
"can_admin_del_admins": true
}

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
environs==9.5.0
aiogram==2.20
peewee==3.14.10
pydantic==1.9.1

0
state/__init__.py Normal file
View File

11
state/checkout.py Normal file
View File

@ -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()

9
state/post.py Normal file
View File

@ -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()

8
state/state.py Normal file
View File

@ -0,0 +1,8 @@
from aiogram.dispatcher.filters.state import StatesGroup, State
class UserState(StatesGroup):
confirm = State()
class AddUser(StatesGroup):
user_id = State()

0
utils/__init__.py Normal file
View File

30
utils/database/base.py Normal file
View File

@ -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()

23
utils/database/cart.py Normal file
View File

@ -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

49
utils/database/market.py Normal file
View File

@ -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()

48
utils/database/model.py Normal file
View File

@ -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])

View File

@ -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

45
utils/database/user.py Normal file
View File

@ -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
)

13
utils/helper.py Normal file
View File

@ -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)

5
utils/json/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from load import permissions as __permissions
from .model import Permission
permission = Permission(**__permissions)

5
utils/json/model.py Normal file
View File

@ -0,0 +1,5 @@
from pydantic import BaseModel
class Permission(BaseModel):
can_admin_add_admins: bool
can_admin_del_admins: bool

54
utils/messages.py Normal file
View File

@ -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"
"Добро пожаловать в магазинчик"
)

23
utils/types.py Normal file
View File

@ -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