old_01
3
.idea/encodings.xml
generated
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding">
|
<component name="Encoding">
|
||||||
<file url="file://$PROJECT_DIR$/old/bot.log" charset="windows-1251" />
|
<file url="file://$PROJECT_DIR$/doners/old/bot.log" charset="windows-1251" />
|
||||||
<file url="file://$PROJECT_DIR$/old/bot.log" charset="windows-1251" />
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
218
README.md
@@ -155,3 +155,221 @@ ChatGPT
|
|||||||
Использовать базу данных для хранения информации о пользователях, заказах и истории коммуникации с операторами.
|
Использовать базу данных для хранения информации о пользователях, заказах и истории коммуникации с операторами.
|
||||||
Реализовать механизм автоматического анализа данных о заказах для предложения персонализированных услуг или скидок.
|
Реализовать механизм автоматического анализа данных о заказах для предложения персонализированных услуг или скидок.
|
||||||
Эти функции помогут сделать бота более удобным и эффективным для пользователей и операторов.
|
Эти функции помогут сделать бота более удобным и эффективным для пользователей и операторов.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Конечно, вот структура файлов для Telegram бота, который использует aiogram, PostgreSQL, logging, inline-кнопки и .env файл для хранения информации о группах админов и операторов. Бот будет делать опросы.
|
||||||
|
|
||||||
|
markdown
|
||||||
|
Копировать код
|
||||||
|
my_telegram_bot/
|
||||||
|
├── .env
|
||||||
|
├── bot.py
|
||||||
|
├── config.py
|
||||||
|
├── database.py
|
||||||
|
├── handlers/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── admin.py
|
||||||
|
│ ├── polls.py
|
||||||
|
│ ├── start.py
|
||||||
|
├── keyboards/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── inline.py
|
||||||
|
├── middlewares/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── logging.py
|
||||||
|
├── models/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── poll.py
|
||||||
|
├── utils/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── misc.py
|
||||||
|
└── requirements.txt
|
||||||
|
Описание файлов и директорий
|
||||||
|
.env: файл с переменными окружения. Пример содержания:
|
||||||
|
|
||||||
|
env
|
||||||
|
Копировать код
|
||||||
|
BOT_TOKEN=<your_bot_token>
|
||||||
|
DATABASE_URL=postgresql://user:password@localhost:5432/mydatabase
|
||||||
|
ADMINS=123456789,987654321
|
||||||
|
OPERATORS=123123123,321321321
|
||||||
|
bot.py: основной файл для запуска бота.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
import logging
|
||||||
|
from aiogram import Bot, Dispatcher, executor
|
||||||
|
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||||
|
from config import load_config
|
||||||
|
from database import setup_database
|
||||||
|
from middlewares.logging import LoggingMiddleware
|
||||||
|
from handlers import start, admin, polls
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
bot = Bot(token=config['BOT_TOKEN'])
|
||||||
|
dp = Dispatcher(bot, storage=MemoryStorage())
|
||||||
|
|
||||||
|
dp.middleware.setup(LoggingMiddleware())
|
||||||
|
|
||||||
|
start.register_handlers(dp)
|
||||||
|
admin.register_handlers(dp)
|
||||||
|
polls.register_handlers(dp)
|
||||||
|
|
||||||
|
setup_database()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
executor.start_polling(dp, skip_updates=True)
|
||||||
|
config.py: файл для загрузки конфигурации из .env.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
return {
|
||||||
|
'BOT_TOKEN': os.getenv('BOT_TOKEN'),
|
||||||
|
'DATABASE_URL': os.getenv('DATABASE_URL'),
|
||||||
|
'ADMINS': list(map(int, os.getenv('ADMINS').split(','))),
|
||||||
|
'OPERATORS': list(map(int, os.getenv('OPERATORS').split(','))),
|
||||||
|
}
|
||||||
|
database.py: файл для настройки базы данных.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from config import load_config
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
engine = sa.create_engine(config['DATABASE_URL'])
|
||||||
|
Session = sessionmaker(bind=engine)
|
||||||
|
|
||||||
|
def setup_database():
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
handlers/__init__.py: инициализация пакета хэндлеров.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from aiogram import Dispatcher
|
||||||
|
|
||||||
|
def register_handlers(dp: Dispatcher):
|
||||||
|
from . import start, admin, polls
|
||||||
|
start.register_handlers(dp)
|
||||||
|
admin.register_handlers(dp)
|
||||||
|
polls.register_handlers(dp)
|
||||||
|
handlers/admin.py: хэндлеры для админов.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from aiogram import types, Dispatcher
|
||||||
|
from config import load_config
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
async def admin_command(message: types.Message):
|
||||||
|
if message.from_user.id not in config['ADMINS']:
|
||||||
|
await message.reply("You are not an admin!")
|
||||||
|
return
|
||||||
|
await message.reply("Hello, Admin!")
|
||||||
|
|
||||||
|
def register_handlers(dp: Dispatcher):
|
||||||
|
dp.register_message_handler(admin_command, commands=['admin'])
|
||||||
|
handlers/polls.py: хэндлеры для опросов.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from aiogram import types, Dispatcher
|
||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from models.poll import Poll
|
||||||
|
from database import Session
|
||||||
|
|
||||||
|
async def create_poll(message: types.Message):
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
markup.add(InlineKeyboardButton('Option 1', callback_data='vote_1'))
|
||||||
|
markup.add(InlineKeyboardButton('Option 2', callback_data='vote_2'))
|
||||||
|
await message.reply("Choose an option:", reply_markup=markup)
|
||||||
|
|
||||||
|
async def handle_vote(callback_query: types.CallbackQuery):
|
||||||
|
option = callback_query.data.split('_')[1]
|
||||||
|
with Session() as session:
|
||||||
|
poll = Poll(option=option)
|
||||||
|
session.add(poll)
|
||||||
|
session.commit()
|
||||||
|
await callback_query.answer(f"You voted for option {option}")
|
||||||
|
|
||||||
|
def register_handlers(dp: Dispatcher):
|
||||||
|
dp.register_message_handler(create_poll, commands=['poll'])
|
||||||
|
dp.register_callback_query_handler(handle_vote, lambda c: c.data.startswith('vote_'))
|
||||||
|
handlers/start.py: хэндлер для команды /start.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from aiogram import types, Dispatcher
|
||||||
|
|
||||||
|
async def start_command(message: types.Message):
|
||||||
|
await message.reply("Hello! I am your polling bot.")
|
||||||
|
|
||||||
|
def register_handlers(dp: Dispatcher):
|
||||||
|
dp.register_message_handler(start_command, commands=['start'])
|
||||||
|
keyboards/inline.py: файл для создания inline-клавиатур.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
|
||||||
|
def create_poll_keyboard():
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
markup.add(InlineKeyboardButton('Option 1', callback_data='vote_1'))
|
||||||
|
markup.add(InlineKeyboardButton('Option 2', callback_data='vote_2'))
|
||||||
|
return markup
|
||||||
|
middlewares/logging.py: файл для логирования.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from aiogram.dispatcher.middlewares import BaseMiddleware
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class LoggingMiddleware(BaseMiddleware):
|
||||||
|
async def on_process_message(self, message, data):
|
||||||
|
logging.info(f"Received message from {message.from_user.id}: {message.text}")
|
||||||
|
models/poll.py: модель для хранения данных опросов.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
from database import Base
|
||||||
|
|
||||||
|
class Poll(Base):
|
||||||
|
__tablename__ = 'polls'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
option = Column(String, nullable=False)
|
||||||
|
utils/misc.py: утилитарные функции.
|
||||||
|
|
||||||
|
python
|
||||||
|
Копировать код
|
||||||
|
# В этом файле можно разместить различные вспомогательные функции.
|
||||||
|
requirements.txt: зависимости проекта.
|
||||||
|
|
||||||
|
Копировать код
|
||||||
|
aiogram
|
||||||
|
python-dotenv
|
||||||
|
sqlalchemy
|
||||||
|
Эта структура предоставляет базу для дальнейшего расширения функциональности бота. Вы можете добавлять дополнительные обработчики, модели и утилиты по мере необходимости.
|
||||||
24
bot.py
@@ -0,0 +1,24 @@
|
|||||||
|
import logging
|
||||||
|
from aiogram import Bot, Dispatcher, executor
|
||||||
|
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||||
|
from config import load_config
|
||||||
|
from database import setup_database
|
||||||
|
from middlewares.logging import LoggingMiddleware
|
||||||
|
from handlers import start, admin, polls
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
bot = Bot(token=config['BOT_TOKEN'])
|
||||||
|
dp = Dispatcher(bot, storage=MemoryStorage())
|
||||||
|
|
||||||
|
dp.middleware.setup(LoggingMiddleware())
|
||||||
|
|
||||||
|
start.register_handlers(dp)
|
||||||
|
admin.register_handlers(dp)
|
||||||
|
polls.register_handlers(dp)
|
||||||
|
|
||||||
|
setup_database()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
executor.start_polling(dp, skip_updates=True)
|
||||||
|
|||||||
12
config.py
@@ -0,0 +1,12 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
return {
|
||||||
|
'BOT_TOKEN': os.getenv('BOT_TOKEN'),
|
||||||
|
'DATABASE_URL': os.getenv('DATABASE_URL'),
|
||||||
|
'ADMINS': list(map(int, os.getenv('ADMINS').split(','))),
|
||||||
|
'OPERATORS': list(map(int, os.getenv('OPERATORS').split(','))),
|
||||||
|
}
|
||||||
|
|||||||
13
database.py
@@ -0,0 +1,13 @@
|
|||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from config import load_config
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
engine = sa.create_engine(config['DATABASE_URL'])
|
||||||
|
Session = sessionmaker(bind=engine)
|
||||||
|
|
||||||
|
def setup_database():
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|||||||
14
doners/Shop-bot/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Cache files
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# Project files
|
||||||
|
.idea/
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
|
*.session
|
||||||
|
data/config.py
|
||||||
|
data/database.db
|
||||||
|
|
||||||
|
# vscode
|
||||||
|
.vscode/
|
||||||
|
.history/
|
||||||
9
doners/Shop-bot/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
FROM python:3.7-slim
|
||||||
|
|
||||||
|
WORKDIR /botname
|
||||||
|
|
||||||
|
COPY requirements.txt /botname/
|
||||||
|
RUN pip install -r /botname/requirements.txt
|
||||||
|
COPY . /botname/
|
||||||
|
|
||||||
|
CMD python3 /botname/app.py
|
||||||
21
doners/Shop-bot/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Hagai
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
47
doners/Shop-bot/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://t.me/example_store_bot"><img src="data/assets/logo.png" alt="ShopBot"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
This is an example Telegram shop bot. It's a simple and, most importantly, efficient way to place an order without leaving your favorite messenger.
|
||||||
|
|
||||||
|
## What can it do?
|
||||||
|
|
||||||
|
1. `/start` - needed to start the bot and choose the mode (user/admin).
|
||||||
|
|
||||||
|
2. `/menu` - go to the menu.
|
||||||
|
|
||||||
|
3. `/sos` - ask the administrator a question.
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
The user menu looks like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Catalog
|
||||||
|
|
||||||
|
The catalog consists of products sorted by categories. Users can add items to their cart, and the admin has full control over catalog management (addition/removal).
|
||||||
|
|
||||||
|
## Cart
|
||||||
|
|
||||||
|
The ordering process looks like this: the user goes to the `🛍️ Catalog`, selects the desired category, chooses products, and clicks the `🛒 Cart` button.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
Then, after making sure everything is in place, proceed to checkout by clicking `📦 Place Order`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Add a Product
|
||||||
|
|
||||||
|
To add a product, select a category and click the `➕ Add Product` button. Then, fill out the "name-description-image-price" form and confirm.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Contacting Administration
|
||||||
|
|
||||||
|
To ask the admin a question, simply select the `/sos` command. There is a limit on the number of questions.
|
||||||
|
|
||||||
|

|
||||||
0
doners/Shop-bot/__init__.py
Normal file
92
doners/Shop-bot/app.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
import os
|
||||||
|
import handlers
|
||||||
|
from aiogram import executor, types
|
||||||
|
from aiogram.types import ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||||
|
from data import config
|
||||||
|
from loader import dp, db, bot
|
||||||
|
import filters
|
||||||
|
import logging
|
||||||
|
|
||||||
|
filters.setup(dp)
|
||||||
|
|
||||||
|
WEBAPP_HOST = "0.0.0.0"
|
||||||
|
WEBAPP_PORT = int(os.environ.get("PORT", 5000))
|
||||||
|
user_message = 'Пользователь'
|
||||||
|
admin_message = 'Админ'
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(commands='start')
|
||||||
|
async def cmd_start(message: types.Message):
|
||||||
|
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
|
||||||
|
markup.row(user_message, admin_message)
|
||||||
|
|
||||||
|
await message.answer('''Привет! 👋
|
||||||
|
|
||||||
|
🤖 Я бот-магазин по подаже товаров любой категории.
|
||||||
|
|
||||||
|
🛍️ Чтобы перейти в каталог и выбрать приглянувшиеся товары возпользуйтесь командой /menu.
|
||||||
|
|
||||||
|
💰 Пополнить счет можно через Яндекс.кассу, Сбербанк или Qiwi.
|
||||||
|
|
||||||
|
❓ Возникли вопросы? Не проблема! Команда /sos поможет связаться с админами, которые постараются как можно быстрее откликнуться.
|
||||||
|
|
||||||
|
🤝 Заказать похожего бота? Свяжитесь с разработчиком <a href="https://t.me/NikolaySimakov">Nikolay Simakov</a>, он не кусается)))
|
||||||
|
''', reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(text=user_message)
|
||||||
|
async def user_mode(message: types.Message):
|
||||||
|
|
||||||
|
cid = message.chat.id
|
||||||
|
if cid in config.ADMINS:
|
||||||
|
config.ADMINS.remove(cid)
|
||||||
|
|
||||||
|
await message.answer('Включен пользовательский режим.', reply_markup=ReplyKeyboardRemove())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(text=admin_message)
|
||||||
|
async def admin_mode(message: types.Message):
|
||||||
|
|
||||||
|
cid = message.chat.id
|
||||||
|
if cid not in config.ADMINS:
|
||||||
|
config.ADMINS.append(cid)
|
||||||
|
|
||||||
|
await message.answer('Включен админский режим.', reply_markup=ReplyKeyboardRemove())
|
||||||
|
|
||||||
|
|
||||||
|
async def on_startup(dp):
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
db.create_tables()
|
||||||
|
|
||||||
|
await bot.delete_webhook()
|
||||||
|
await bot.set_webhook(config.WEBHOOK_URL)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_shutdown():
|
||||||
|
logging.warning("Shutting down..")
|
||||||
|
await bot.delete_webhook()
|
||||||
|
await dp.storage.close()
|
||||||
|
await dp.storage.wait_closed()
|
||||||
|
logging.warning("Bot down")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
if "HEROKU" in list(os.environ.keys()):
|
||||||
|
|
||||||
|
executor.start_webhook(
|
||||||
|
dispatcher=dp,
|
||||||
|
webhook_path=config.WEBHOOK_PATH,
|
||||||
|
on_startup=on_startup,
|
||||||
|
on_shutdown=on_shutdown,
|
||||||
|
skip_updates=True,
|
||||||
|
host=WEBAPP_HOST,
|
||||||
|
port=WEBAPP_PORT,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
executor.start_polling(dp, on_startup=on_startup, skip_updates=False)
|
||||||
0
doners/Shop-bot/data/__init__.py
Normal file
BIN
doners/Shop-bot/data/assets/1.png
Normal file
|
After Width: | Height: | Size: 616 KiB |
BIN
doners/Shop-bot/data/assets/2.png
Normal file
|
After Width: | Height: | Size: 947 KiB |
BIN
doners/Shop-bot/data/assets/3.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
doners/Shop-bot/data/assets/4.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
doners/Shop-bot/data/assets/5.png
Normal file
|
After Width: | Height: | Size: 647 KiB |
BIN
doners/Shop-bot/data/assets/6.png
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
doners/Shop-bot/data/assets/7.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
doners/Shop-bot/data/assets/logo.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
doners/Shop-bot/data/assets/logo_mini.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
7
doners/Shop-bot/filters/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from aiogram import Dispatcher
|
||||||
|
from .is_admin import IsAdmin
|
||||||
|
from .is_user import IsUser
|
||||||
|
|
||||||
|
def setup(dp: Dispatcher):
|
||||||
|
dp.filters_factory.bind(IsAdmin, event_handlers=[dp.message_handlers])
|
||||||
|
dp.filters_factory.bind(IsUser, event_handlers=[dp.message_handlers])
|
||||||
9
doners/Shop-bot/filters/is_admin.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
from aiogram.types import Message
|
||||||
|
from aiogram.dispatcher.filters import BoundFilter
|
||||||
|
from data.config import ADMINS
|
||||||
|
|
||||||
|
class IsAdmin(BoundFilter):
|
||||||
|
|
||||||
|
async def check(self, message: Message):
|
||||||
|
return message.from_user.id in ADMINS
|
||||||
9
doners/Shop-bot/filters/is_user.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
from aiogram.types import Message
|
||||||
|
from aiogram.dispatcher.filters import BoundFilter
|
||||||
|
from data.config import ADMINS
|
||||||
|
|
||||||
|
class IsUser(BoundFilter):
|
||||||
|
|
||||||
|
async def check(self, message: Message):
|
||||||
|
return message.from_user.id not in ADMINS
|
||||||
4
doners/Shop-bot/handlers/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .admin import dp
|
||||||
|
from .user import dp
|
||||||
|
|
||||||
|
__all__ = ['dp']
|
||||||
5
doners/Shop-bot/handlers/admin/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .add import dp
|
||||||
|
from .questions import dp
|
||||||
|
from .orders import dp
|
||||||
|
|
||||||
|
__all__ = ['dp']
|
||||||
285
doners/Shop-bot/handlers/admin/add.py
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
|
||||||
|
from aiogram.dispatcher import FSMContext
|
||||||
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, ContentType, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from keyboards.default.markups import *
|
||||||
|
from states import ProductState, CategoryState
|
||||||
|
from aiogram.types.chat import ChatActions
|
||||||
|
from doners.old_2.handlers import settings
|
||||||
|
from loader import dp, db, bot
|
||||||
|
from filters import IsAdmin
|
||||||
|
from hashlib import md5
|
||||||
|
|
||||||
|
|
||||||
|
category_cb = CallbackData('category', 'id', 'action')
|
||||||
|
product_cb = CallbackData('product', 'id', 'action')
|
||||||
|
|
||||||
|
add_product = '➕ Добавить товар'
|
||||||
|
delete_category = '🗑️ Удалить категорию'
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=settings)
|
||||||
|
async def process_settings(message: Message):
|
||||||
|
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
|
||||||
|
for idx, title in db.fetchall('SELECT * FROM categories'):
|
||||||
|
|
||||||
|
markup.add(InlineKeyboardButton(
|
||||||
|
title, callback_data=category_cb.new(id=idx, action='view')))
|
||||||
|
|
||||||
|
markup.add(InlineKeyboardButton(
|
||||||
|
'+ Добавить категорию', callback_data='add_category'))
|
||||||
|
|
||||||
|
await message.answer('Настройка категорий:', reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsAdmin(), category_cb.filter(action='view'))
|
||||||
|
async def category_callback_handler(query: CallbackQuery, callback_data: dict, state: FSMContext):
|
||||||
|
|
||||||
|
category_idx = callback_data['id']
|
||||||
|
|
||||||
|
products = db.fetchall('''SELECT * FROM products product
|
||||||
|
WHERE product.tag = (SELECT title FROM categories WHERE idx=?)''',
|
||||||
|
(category_idx,))
|
||||||
|
|
||||||
|
await query.message.delete()
|
||||||
|
await query.answer('Все добавленные товары в эту категорию.')
|
||||||
|
await state.update_data(category_index=category_idx)
|
||||||
|
await show_products(query.message, products, category_idx)
|
||||||
|
|
||||||
|
|
||||||
|
# category
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsAdmin(), text='add_category')
|
||||||
|
async def add_category_callback_handler(query: CallbackQuery):
|
||||||
|
await query.message.delete()
|
||||||
|
await query.message.answer('Название категории?')
|
||||||
|
await CategoryState.title.set()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), state=CategoryState.title)
|
||||||
|
async def set_category_title_handler(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
category = message.text
|
||||||
|
idx = md5(category.encode('utf-8')).hexdigest()
|
||||||
|
db.query('INSERT INTO categories VALUES (?, ?)', (idx, category))
|
||||||
|
|
||||||
|
await state.finish()
|
||||||
|
await process_settings(message)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=delete_category)
|
||||||
|
async def delete_category_handler(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
if 'category_index' in data.keys():
|
||||||
|
|
||||||
|
idx = data['category_index']
|
||||||
|
|
||||||
|
db.query(
|
||||||
|
'DELETE FROM products WHERE tag IN (SELECT title FROM categories WHERE idx=?)', (idx,))
|
||||||
|
db.query('DELETE FROM categories WHERE idx=?', (idx,))
|
||||||
|
|
||||||
|
await message.answer('Готово!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await process_settings(message)
|
||||||
|
|
||||||
|
|
||||||
|
# add product
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=add_product)
|
||||||
|
async def process_add_product(message: Message):
|
||||||
|
|
||||||
|
await ProductState.title.set()
|
||||||
|
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
markup.add(cancel_message)
|
||||||
|
|
||||||
|
await message.answer('Название?', reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=cancel_message, state=ProductState.title)
|
||||||
|
async def process_cancel(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
await message.answer('Ок, отменено!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await state.finish()
|
||||||
|
|
||||||
|
await process_settings(message)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=back_message, state=ProductState.title)
|
||||||
|
async def process_title_back(message: Message, state: FSMContext):
|
||||||
|
await process_add_product(message)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), state=ProductState.title)
|
||||||
|
async def process_title(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['title'] = message.text
|
||||||
|
|
||||||
|
await ProductState.next()
|
||||||
|
await message.answer('Описание?', reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=back_message, state=ProductState.body)
|
||||||
|
async def process_body_back(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
await ProductState.title.set()
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
await message.answer(f"Изменить название с <b>{data['title']}</b>?", reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), state=ProductState.body)
|
||||||
|
async def process_body(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['body'] = message.text
|
||||||
|
|
||||||
|
await ProductState.next()
|
||||||
|
await message.answer('Фото?', reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), content_types=ContentType.PHOTO, state=ProductState.image)
|
||||||
|
async def process_image_photo(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
fileID = message.photo[-1].file_id
|
||||||
|
file_info = await bot.get_file(fileID)
|
||||||
|
downloaded_file = (await bot.download_file(file_info.file_path)).read()
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['image'] = downloaded_file
|
||||||
|
|
||||||
|
await ProductState.next()
|
||||||
|
await message.answer('Цена?', reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), content_types=ContentType.TEXT, state=ProductState.image)
|
||||||
|
async def process_image_url(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
if message.text == back_message:
|
||||||
|
|
||||||
|
await ProductState.body.set()
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
await message.answer(f"Изменить описание с <b>{data['body']}</b>?", reply_markup=back_markup())
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await message.answer('Вам нужно прислать фото товара.')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), lambda message: not message.text.isdigit(), state=ProductState.price)
|
||||||
|
async def process_price_invalid(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
if message.text == back_message:
|
||||||
|
|
||||||
|
await ProductState.image.set()
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
await message.answer("Другое изображение?", reply_markup=back_markup())
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await message.answer('Укажите цену в виде числа!')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), lambda message: message.text.isdigit(), state=ProductState.price)
|
||||||
|
async def process_price(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
data['price'] = message.text
|
||||||
|
|
||||||
|
title = data['title']
|
||||||
|
body = data['body']
|
||||||
|
price = data['price']
|
||||||
|
|
||||||
|
await ProductState.next()
|
||||||
|
text = f'<b>{title}</b>\n\n{body}\n\nЦена: {price} рублей.'
|
||||||
|
|
||||||
|
markup = check_markup()
|
||||||
|
|
||||||
|
await message.answer_photo(photo=data['image'],
|
||||||
|
caption=text,
|
||||||
|
reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), lambda message: message.text not in [back_message, all_right_message], state=ProductState.confirm)
|
||||||
|
async def process_confirm_invalid(message: Message, state: FSMContext):
|
||||||
|
await message.answer('Такого варианта не было.')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=back_message, state=ProductState.confirm)
|
||||||
|
async def process_confirm_back(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
await ProductState.price.set()
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
await message.answer(f"Изменить цену с <b>{data['price']}</b>?", reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=all_right_message, state=ProductState.confirm)
|
||||||
|
async def process_confirm(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
title = data['title']
|
||||||
|
body = data['body']
|
||||||
|
image = data['image']
|
||||||
|
price = data['price']
|
||||||
|
|
||||||
|
tag = db.fetchone(
|
||||||
|
'SELECT title FROM categories WHERE idx=?', (data['category_index'],))[0]
|
||||||
|
idx = md5(' '.join([title, body, price, tag]
|
||||||
|
).encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
db.query('INSERT INTO products VALUES (?, ?, ?, ?, ?, ?)',
|
||||||
|
(idx, title, body, image, int(price), tag))
|
||||||
|
|
||||||
|
await state.finish()
|
||||||
|
await message.answer('Готово!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await process_settings(message)
|
||||||
|
|
||||||
|
|
||||||
|
# delete product
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsAdmin(), product_cb.filter(action='delete'))
|
||||||
|
async def delete_product_callback_handler(query: CallbackQuery, callback_data: dict):
|
||||||
|
|
||||||
|
product_idx = callback_data['id']
|
||||||
|
db.query('DELETE FROM products WHERE idx=?', (product_idx,))
|
||||||
|
await query.answer('Удалено!')
|
||||||
|
await query.message.delete()
|
||||||
|
|
||||||
|
|
||||||
|
async def show_products(m, products, category_idx):
|
||||||
|
|
||||||
|
await bot.send_chat_action(m.chat.id, ChatActions.TYPING)
|
||||||
|
|
||||||
|
for idx, title, body, image, price, tag in products:
|
||||||
|
|
||||||
|
text = f'<b>{title}</b>\n\n{body}\n\nЦена: {price} рублей.'
|
||||||
|
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
markup.add(InlineKeyboardButton(
|
||||||
|
'🗑️ Удалить', callback_data=product_cb.new(id=idx, action='delete')))
|
||||||
|
|
||||||
|
await m.answer_photo(photo=image,
|
||||||
|
caption=text,
|
||||||
|
reply_markup=markup)
|
||||||
|
|
||||||
|
markup = ReplyKeyboardMarkup()
|
||||||
|
markup.add(add_product)
|
||||||
|
markup.add(delete_category)
|
||||||
|
|
||||||
|
await m.answer('Хотите что-нибудь добавить или удалить?', reply_markup=markup)
|
||||||
22
doners/Shop-bot/handlers/admin/orders.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
from aiogram.types import Message
|
||||||
|
from loader import dp, db
|
||||||
|
from doners.old_2.handlers import orders
|
||||||
|
from filters import IsAdmin
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=orders)
|
||||||
|
async def process_orders(message: Message):
|
||||||
|
|
||||||
|
orders = db.fetchall('SELECT * FROM orders')
|
||||||
|
|
||||||
|
if len(orders) == 0: await message.answer('У вас нет заказов.')
|
||||||
|
else: await order_answer(message, orders)
|
||||||
|
|
||||||
|
async def order_answer(message, orders):
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
res += f'Заказ <b>№{order[3]}</b>\n\n'
|
||||||
|
|
||||||
|
await message.answer(res)
|
||||||
78
doners/Shop-bot/handlers/admin/questions.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
from doners.old_2.handlers import questions
|
||||||
|
from aiogram.dispatcher import FSMContext
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from keyboards.default.markups import all_right_message, cancel_message, submit_markup
|
||||||
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardRemove
|
||||||
|
from aiogram.types.chat import ChatActions
|
||||||
|
from states import AnswerState
|
||||||
|
from loader import dp, db, bot
|
||||||
|
from filters import IsAdmin
|
||||||
|
|
||||||
|
question_cb = CallbackData('question', 'cid', 'action')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=questions)
|
||||||
|
async def process_questions(message: Message):
|
||||||
|
|
||||||
|
await bot.send_chat_action(message.chat.id, ChatActions.TYPING)
|
||||||
|
questions = db.fetchall('SELECT * FROM questions')
|
||||||
|
|
||||||
|
if len(questions) == 0:
|
||||||
|
|
||||||
|
await message.answer('Нет вопросов.')
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
for cid, question in questions:
|
||||||
|
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
markup.add(InlineKeyboardButton(
|
||||||
|
'Ответить', callback_data=question_cb.new(cid=cid, action='answer')))
|
||||||
|
|
||||||
|
await message.answer(question, reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsAdmin(), question_cb.filter(action='answer'))
|
||||||
|
async def process_answer(query: CallbackQuery, callback_data: dict, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['cid'] = callback_data['cid']
|
||||||
|
|
||||||
|
await query.message.answer('Напиши ответ.', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await AnswerState.answer.set()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), state=AnswerState.answer)
|
||||||
|
async def process_submit(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['answer'] = message.text
|
||||||
|
|
||||||
|
await AnswerState.next()
|
||||||
|
await message.answer('Убедитесь, что не ошиблись в ответе.', reply_markup=submit_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=cancel_message, state=AnswerState.submit)
|
||||||
|
async def process_send_answer(message: Message, state: FSMContext):
|
||||||
|
await message.answer('Отменено!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await state.finish()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), text=all_right_message, state=AnswerState.submit)
|
||||||
|
async def process_send_answer(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
answer = data['answer']
|
||||||
|
cid = data['cid']
|
||||||
|
|
||||||
|
question = db.fetchone(
|
||||||
|
'SELECT question FROM questions WHERE cid=?', (cid,))[0]
|
||||||
|
db.query('DELETE FROM questions WHERE cid=?', (cid,))
|
||||||
|
text = f'Вопрос: <b>{question}</b>\n\nОтвет: <b>{answer}</b>'
|
||||||
|
|
||||||
|
await message.answer('Отправлено!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await bot.send_message(cid, text)
|
||||||
|
|
||||||
|
await state.finish()
|
||||||
8
doners/Shop-bot/handlers/user/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from .menu import dp
|
||||||
|
from .cart import dp
|
||||||
|
from .wallet import dp
|
||||||
|
from .catalog import dp
|
||||||
|
from .delivery_status import dp
|
||||||
|
from .sos import dp
|
||||||
|
|
||||||
|
__all__ = ['dp']
|
||||||
247
doners/Shop-bot/handlers/user/cart.py
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
import logging
|
||||||
|
from aiogram.dispatcher import FSMContext
|
||||||
|
from aiogram.types import Message, CallbackQuery, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from keyboards.inline.products_from_cart import product_markup, product_cb
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from keyboards.default.markups import *
|
||||||
|
from aiogram.types.chat import ChatActions
|
||||||
|
from states import CheckoutState
|
||||||
|
from loader import dp, db, bot
|
||||||
|
from filters import IsUser
|
||||||
|
from .menu import cart
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=cart)
|
||||||
|
async def process_cart(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
cart_data = db.fetchall(
|
||||||
|
'SELECT * FROM cart WHERE cid=?', (message.chat.id,))
|
||||||
|
|
||||||
|
if len(cart_data) == 0:
|
||||||
|
|
||||||
|
await message.answer('Ваша корзина пуста.')
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await bot.send_chat_action(message.chat.id, ChatActions.TYPING)
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['products'] = {}
|
||||||
|
|
||||||
|
order_cost = 0
|
||||||
|
|
||||||
|
for _, idx, count_in_cart in cart_data:
|
||||||
|
|
||||||
|
product = db.fetchone('SELECT * FROM products WHERE idx=?', (idx,))
|
||||||
|
|
||||||
|
if product == None:
|
||||||
|
|
||||||
|
db.query('DELETE FROM cart WHERE idx=?', (idx,))
|
||||||
|
|
||||||
|
else:
|
||||||
|
_, title, body, image, price, _ = product
|
||||||
|
order_cost += price
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['products'][idx] = [title, price, count_in_cart]
|
||||||
|
|
||||||
|
markup = product_markup(idx, count_in_cart)
|
||||||
|
text = f'<b>{title}</b>\n\n{body}\n\nЦена: {price}₽.'
|
||||||
|
|
||||||
|
await message.answer_photo(photo=image,
|
||||||
|
caption=text,
|
||||||
|
reply_markup=markup)
|
||||||
|
|
||||||
|
if order_cost != 0:
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||||
|
markup.add('📦 Оформить заказ')
|
||||||
|
|
||||||
|
await message.answer('Перейти к оформлению?',
|
||||||
|
reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsUser(), product_cb.filter(action='count'))
|
||||||
|
@dp.callback_query_handler(IsUser(), product_cb.filter(action='increase'))
|
||||||
|
@dp.callback_query_handler(IsUser(), product_cb.filter(action='decrease'))
|
||||||
|
async def product_callback_handler(query: CallbackQuery, callback_data: dict, state: FSMContext):
|
||||||
|
|
||||||
|
idx = callback_data['id']
|
||||||
|
action = callback_data['action']
|
||||||
|
|
||||||
|
if 'count' == action:
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
if 'products' not in data.keys():
|
||||||
|
|
||||||
|
await process_cart(query.message, state)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await query.answer('Количество - ' + data['products'][idx][2])
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
if 'products' not in data.keys():
|
||||||
|
|
||||||
|
await process_cart(query.message, state)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
data['products'][idx][2] += 1 if 'increase' == action else -1
|
||||||
|
count_in_cart = data['products'][idx][2]
|
||||||
|
|
||||||
|
if count_in_cart == 0:
|
||||||
|
|
||||||
|
db.query('''DELETE FROM cart
|
||||||
|
WHERE cid = ? AND idx = ?''', (query.message.chat.id, idx))
|
||||||
|
|
||||||
|
await query.message.delete()
|
||||||
|
else:
|
||||||
|
|
||||||
|
db.query('''UPDATE cart
|
||||||
|
SET quantity = ?
|
||||||
|
WHERE cid = ? AND idx = ?''', (count_in_cart, query.message.chat.id, idx))
|
||||||
|
|
||||||
|
await query.message.edit_reply_markup(product_markup(idx, count_in_cart))
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text='📦 Оформить заказ')
|
||||||
|
async def process_checkout(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
await CheckoutState.check_cart.set()
|
||||||
|
await checkout(message, state)
|
||||||
|
|
||||||
|
|
||||||
|
async def checkout(message, state):
|
||||||
|
answer = ''
|
||||||
|
total_price = 0
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
for title, price, count_in_cart in data['products'].values():
|
||||||
|
|
||||||
|
tp = count_in_cart * price
|
||||||
|
answer += f'<b>{title}</b> * {count_in_cart}шт. = {tp}₽\n'
|
||||||
|
total_price += tp
|
||||||
|
|
||||||
|
await message.answer(f'{answer}\nОбщая сумма заказа: {total_price}₽.',
|
||||||
|
reply_markup=check_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), lambda message: message.text not in [all_right_message, back_message], state=CheckoutState.check_cart)
|
||||||
|
async def process_check_cart_invalid(message: Message):
|
||||||
|
await message.reply('Такого варианта не было.')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.check_cart)
|
||||||
|
async def process_check_cart_back(message: Message, state: FSMContext):
|
||||||
|
await state.finish()
|
||||||
|
await process_cart(message, state)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=all_right_message, state=CheckoutState.check_cart)
|
||||||
|
async def process_check_cart_all_right(message: Message, state: FSMContext):
|
||||||
|
await CheckoutState.next()
|
||||||
|
await message.answer('Укажите свое имя.',
|
||||||
|
reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.name)
|
||||||
|
async def process_name_back(message: Message, state: FSMContext):
|
||||||
|
await CheckoutState.check_cart.set()
|
||||||
|
await checkout(message, state)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), state=CheckoutState.name)
|
||||||
|
async def process_name(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
data['name'] = message.text
|
||||||
|
|
||||||
|
if 'address' in data.keys():
|
||||||
|
|
||||||
|
await confirm(message)
|
||||||
|
await CheckoutState.confirm.set()
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await CheckoutState.next()
|
||||||
|
await message.answer('Укажите свой адрес места жительства.',
|
||||||
|
reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.address)
|
||||||
|
async def process_address_back(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
await message.answer('Изменить имя с <b>' + data['name'] + '</b>?',
|
||||||
|
reply_markup=back_markup())
|
||||||
|
|
||||||
|
await CheckoutState.name.set()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), state=CheckoutState.address)
|
||||||
|
async def process_address(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['address'] = message.text
|
||||||
|
|
||||||
|
await confirm(message)
|
||||||
|
await CheckoutState.next()
|
||||||
|
|
||||||
|
|
||||||
|
async def confirm(message):
|
||||||
|
|
||||||
|
await message.answer('Убедитесь, что все правильно оформлено и подтвердите заказ.',
|
||||||
|
reply_markup=confirm_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), lambda message: message.text not in [confirm_message, back_message], state=CheckoutState.confirm)
|
||||||
|
async def process_confirm_invalid(message: Message):
|
||||||
|
await message.reply('Такого варианта не было.')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=back_message, state=CheckoutState.confirm)
|
||||||
|
async def process_confirm(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
await CheckoutState.address.set()
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
await message.answer('Изменить адрес с <b>' + data['address'] + '</b>?',
|
||||||
|
reply_markup=back_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=confirm_message, state=CheckoutState.confirm)
|
||||||
|
async def process_confirm(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
enough_money = True # enough money on the balance sheet
|
||||||
|
markup = ReplyKeyboardRemove()
|
||||||
|
|
||||||
|
if enough_money:
|
||||||
|
|
||||||
|
logging.info('Deal was made.')
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
|
||||||
|
cid = message.chat.id
|
||||||
|
products = [idx + '=' + str(quantity)
|
||||||
|
for idx, quantity in db.fetchall('''SELECT idx, quantity FROM cart
|
||||||
|
WHERE cid=?''', (cid,))] # idx=quantity
|
||||||
|
|
||||||
|
db.query('INSERT INTO orders VALUES (?, ?, ?, ?)',
|
||||||
|
(cid, data['name'], data['address'], ' '.join(products)))
|
||||||
|
|
||||||
|
db.query('DELETE FROM cart WHERE cid=?', (cid,))
|
||||||
|
|
||||||
|
await message.answer('Ок! Ваш заказ уже в пути 🚀\nИмя: <b>' + data['name'] + '</b>\nАдрес: <b>' + data['address'] + '</b>',
|
||||||
|
reply_markup=markup)
|
||||||
|
else:
|
||||||
|
|
||||||
|
await message.answer('У вас недостаточно денег на счете. Пополните баланс!',
|
||||||
|
reply_markup=markup)
|
||||||
|
|
||||||
|
await state.finish()
|
||||||
58
doners/Shop-bot/handlers/user/catalog.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
import logging
|
||||||
|
from aiogram.types import Message, CallbackQuery
|
||||||
|
from keyboards.inline.categories import categories_markup, category_cb
|
||||||
|
from keyboards.inline.products_from_catalog import product_markup, product_cb
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from aiogram.types.chat import ChatActions
|
||||||
|
from loader import dp, db, bot
|
||||||
|
from .menu import catalog
|
||||||
|
from filters import IsUser
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=catalog)
|
||||||
|
async def process_catalog(message: Message):
|
||||||
|
await message.answer('Выберите раздел, чтобы вывести список товаров:',
|
||||||
|
reply_markup=categories_markup())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsUser(), category_cb.filter(action='view'))
|
||||||
|
async def category_callback_handler(query: CallbackQuery, callback_data: dict):
|
||||||
|
|
||||||
|
products = db.fetchall('''SELECT * FROM products product
|
||||||
|
WHERE product.tag = (SELECT title FROM categories WHERE idx=?)
|
||||||
|
AND product.idx NOT IN (SELECT idx FROM cart WHERE cid = ?)''',
|
||||||
|
(callback_data['id'], query.message.chat.id))
|
||||||
|
|
||||||
|
await query.answer('Все доступные товары.')
|
||||||
|
await show_products(query.message, products)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(IsUser(), product_cb.filter(action='add'))
|
||||||
|
async def add_product_callback_handler(query: CallbackQuery, callback_data: dict):
|
||||||
|
|
||||||
|
db.query('INSERT INTO cart VALUES (?, ?, 1)',
|
||||||
|
(query.message.chat.id, callback_data['id']))
|
||||||
|
|
||||||
|
await query.answer('Товар добавлен в корзину!')
|
||||||
|
await query.message.delete()
|
||||||
|
|
||||||
|
|
||||||
|
async def show_products(m, products):
|
||||||
|
|
||||||
|
if len(products) == 0:
|
||||||
|
|
||||||
|
await m.answer('Здесь ничего нет 😢')
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await bot.send_chat_action(m.chat.id, ChatActions.TYPING)
|
||||||
|
|
||||||
|
for idx, title, body, image, price, _ in products:
|
||||||
|
|
||||||
|
markup = product_markup(idx, price)
|
||||||
|
text = f'<b>{title}</b>\n\n{body}'
|
||||||
|
|
||||||
|
await m.answer_photo(photo=image,
|
||||||
|
caption=text,
|
||||||
|
reply_markup=markup)
|
||||||
31
doners/Shop-bot/handlers/user/delivery_status.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
from aiogram.types import Message
|
||||||
|
from loader import dp, db
|
||||||
|
from .menu import delivery_status
|
||||||
|
from filters import IsUser
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=delivery_status)
|
||||||
|
async def process_delivery_status(message: Message):
|
||||||
|
|
||||||
|
orders = db.fetchall('SELECT * FROM orders WHERE cid=?', (message.chat.id,))
|
||||||
|
|
||||||
|
if len(orders) == 0: await message.answer('У вас нет активных заказов.')
|
||||||
|
else: await delivery_status_answer(message, orders)
|
||||||
|
|
||||||
|
async def delivery_status_answer(message, orders):
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
|
||||||
|
res += f'Заказ <b>№{order[3]}</b>'
|
||||||
|
answer = [
|
||||||
|
' лежит на складе.',
|
||||||
|
' уже в пути!',
|
||||||
|
' прибыл и ждет вас на почте!'
|
||||||
|
]
|
||||||
|
|
||||||
|
res += answer[0]
|
||||||
|
res += '\n\n'
|
||||||
|
|
||||||
|
await message.answer(res)
|
||||||
30
doners/Shop-bot/handlers/user/menu.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
from aiogram.types import Message, CallbackQuery, ReplyKeyboardMarkup
|
||||||
|
from loader import dp
|
||||||
|
from filters import IsAdmin, IsUser
|
||||||
|
|
||||||
|
catalog = '🛍️ Каталог'
|
||||||
|
balance = '💰 Баланс'
|
||||||
|
cart = '🛒 Корзина'
|
||||||
|
delivery_status = '🚚 Статус заказа'
|
||||||
|
|
||||||
|
settings = '⚙️ Настройка каталога'
|
||||||
|
orders = '🚚 Заказы'
|
||||||
|
questions = '❓ Вопросы'
|
||||||
|
|
||||||
|
@dp.message_handler(IsAdmin(), commands='menu')
|
||||||
|
async def admin_menu(message: Message):
|
||||||
|
markup = ReplyKeyboardMarkup(selective=True)
|
||||||
|
markup.add(settings)
|
||||||
|
markup.add(questions, orders)
|
||||||
|
|
||||||
|
await message.answer('Меню', reply_markup=markup)
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), commands='menu')
|
||||||
|
async def user_menu(message: Message):
|
||||||
|
markup = ReplyKeyboardMarkup(selective=True)
|
||||||
|
markup.add(catalog)
|
||||||
|
markup.add(balance, cart)
|
||||||
|
markup.add(delivery_status)
|
||||||
|
|
||||||
|
await message.answer('Меню', reply_markup=markup)
|
||||||
54
doners/Shop-bot/handlers/user/sos.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
from aiogram.dispatcher import FSMContext
|
||||||
|
from aiogram.types import ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||||
|
from keyboards.default.markups import all_right_message, cancel_message, submit_markup
|
||||||
|
from aiogram.types import Message
|
||||||
|
from states import SosState
|
||||||
|
from filters import IsUser
|
||||||
|
from loader import dp, db
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(commands='sos')
|
||||||
|
async def cmd_sos(message: Message):
|
||||||
|
await SosState.question.set()
|
||||||
|
await message.answer('В чем суть проблемы? Опишите как можно детальнее и администратор обязательно вам ответит.', reply_markup=ReplyKeyboardRemove())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(state=SosState.question)
|
||||||
|
async def process_question(message: Message, state: FSMContext):
|
||||||
|
async with state.proxy() as data:
|
||||||
|
data['question'] = message.text
|
||||||
|
|
||||||
|
await message.answer('Убедитесь, что все верно.', reply_markup=submit_markup())
|
||||||
|
await SosState.next()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(lambda message: message.text not in [cancel_message, all_right_message], state=SosState.submit)
|
||||||
|
async def process_price_invalid(message: Message):
|
||||||
|
await message.answer('Такого варианта не было.')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(text=cancel_message, state=SosState.submit)
|
||||||
|
async def process_cancel(message: Message, state: FSMContext):
|
||||||
|
await message.answer('Отменено!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
await state.finish()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(text=all_right_message, state=SosState.submit)
|
||||||
|
async def process_submit(message: Message, state: FSMContext):
|
||||||
|
|
||||||
|
cid = message.chat.id
|
||||||
|
|
||||||
|
if db.fetchone('SELECT * FROM questions WHERE cid=?', (cid,)) == None:
|
||||||
|
|
||||||
|
async with state.proxy() as data:
|
||||||
|
db.query('INSERT INTO questions VALUES (?, ?)',
|
||||||
|
(cid, data['question']))
|
||||||
|
|
||||||
|
await message.answer('Отправлено!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
await message.answer('Превышен лимит на количество задаваемых вопросов.', reply_markup=ReplyKeyboardRemove())
|
||||||
|
|
||||||
|
await state.finish()
|
||||||
18
doners/Shop-bot/handlers/user/wallet.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
from loader import dp
|
||||||
|
from aiogram.dispatcher import FSMContext
|
||||||
|
from aiogram.types import Message
|
||||||
|
from filters import IsUser
|
||||||
|
from .menu import balance
|
||||||
|
|
||||||
|
# test card ==> 1111 1111 1111 1026, 12/22, CVC 000
|
||||||
|
|
||||||
|
# shopId 506751
|
||||||
|
|
||||||
|
# shopArticleId 538350
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(IsUser(), text=balance)
|
||||||
|
async def process_balance(message: Message, state: FSMContext):
|
||||||
|
await message.answer('Ваш кошелек пуст! Чтобы его пополнить нужно...')
|
||||||
|
|
||||||
2
doners/Shop-bot/keyboards/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import inline
|
||||||
|
from . import default
|
||||||
1
doners/Shop-bot/keyboards/default/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import markups
|
||||||
31
doners/Shop-bot/keyboards/default/markups.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from aiogram.types import ReplyKeyboardMarkup
|
||||||
|
|
||||||
|
back_message = '👈 Назад'
|
||||||
|
confirm_message = '✅ Подтвердить заказ'
|
||||||
|
all_right_message = '✅ Все верно'
|
||||||
|
cancel_message = '🚫 Отменить'
|
||||||
|
|
||||||
|
def confirm_markup():
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||||
|
markup.add(confirm_message)
|
||||||
|
markup.add(back_message)
|
||||||
|
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def back_markup():
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||||
|
markup.add(back_message)
|
||||||
|
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def check_markup():
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||||
|
markup.row(back_message, all_right_message)
|
||||||
|
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def submit_markup():
|
||||||
|
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
||||||
|
markup.row(cancel_message, all_right_message)
|
||||||
|
|
||||||
|
return markup
|
||||||
3
doners/Shop-bot/keyboards/inline/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from . import products_from_catalog
|
||||||
|
from . import products_from_cart
|
||||||
|
from . import categories
|
||||||
16
doners/Shop-bot/keyboards/inline/categories.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from loader import db
|
||||||
|
|
||||||
|
category_cb = CallbackData('category', 'id', 'action')
|
||||||
|
|
||||||
|
|
||||||
|
def categories_markup():
|
||||||
|
|
||||||
|
global category_cb
|
||||||
|
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
for idx, title in db.fetchall('SELECT * FROM categories'):
|
||||||
|
markup.add(InlineKeyboardButton(title, callback_data=category_cb.new(id=idx, action='view')))
|
||||||
|
|
||||||
|
return markup
|
||||||
16
doners/Shop-bot/keyboards/inline/products_from_cart.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
|
||||||
|
product_cb = CallbackData('product', 'id', 'action')
|
||||||
|
|
||||||
|
def product_markup(idx, count):
|
||||||
|
|
||||||
|
global product_cb
|
||||||
|
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
back_btn = InlineKeyboardButton('⬅️', callback_data=product_cb.new(id=idx, action='decrease'))
|
||||||
|
count_btn = InlineKeyboardButton(count, callback_data=product_cb.new(id=idx, action='count'))
|
||||||
|
next_btn = InlineKeyboardButton('➡️', callback_data=product_cb.new(id=idx, action='increase'))
|
||||||
|
markup.row(back_btn, count_btn, next_btn)
|
||||||
|
|
||||||
|
return markup
|
||||||
15
doners/Shop-bot/keyboards/inline/products_from_catalog.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from loader import db
|
||||||
|
|
||||||
|
product_cb = CallbackData('product', 'id', 'action')
|
||||||
|
|
||||||
|
|
||||||
|
def product_markup(idx='', price=0):
|
||||||
|
|
||||||
|
global product_cb
|
||||||
|
|
||||||
|
markup = InlineKeyboardMarkup()
|
||||||
|
markup.add(InlineKeyboardButton(f'Добавить в корзину - {price}₽', callback_data=product_cb.new(id=idx, action='add')))
|
||||||
|
|
||||||
|
return markup
|
||||||
10
doners/Shop-bot/loader.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from aiogram import Bot, Dispatcher, types
|
||||||
|
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||||
|
from utils.db.storage import DatabaseManager
|
||||||
|
|
||||||
|
from data import config
|
||||||
|
|
||||||
|
bot = Bot(token=config.BOT_TOKEN, parse_mode=types.ParseMode.HTML)
|
||||||
|
storage = MemoryStorage()
|
||||||
|
dp = Dispatcher(bot, storage=storage)
|
||||||
|
db = DatabaseManager('data/database.db')
|
||||||
1
doners/Shop-bot/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
aiogram==2.9.2
|
||||||
3
doners/Shop-bot/states/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .checkout_state import CheckoutState
|
||||||
|
from .product_state import ProductState, CategoryState
|
||||||
|
from .sos_state import SosState, AnswerState
|
||||||
7
doners/Shop-bot/states/checkout_state.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||||
|
|
||||||
|
class CheckoutState(StatesGroup):
|
||||||
|
check_cart = State()
|
||||||
|
name = State()
|
||||||
|
address = State()
|
||||||
|
confirm = State()
|
||||||
11
doners/Shop-bot/states/product_state.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||||
|
|
||||||
|
class ProductState(StatesGroup):
|
||||||
|
title = State()
|
||||||
|
body = State()
|
||||||
|
image = State()
|
||||||
|
price = State()
|
||||||
|
confirm = State()
|
||||||
|
|
||||||
|
class CategoryState(StatesGroup):
|
||||||
|
title = State()
|
||||||
9
doners/Shop-bot/states/sos_state.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||||
|
|
||||||
|
class SosState(StatesGroup):
|
||||||
|
question = State()
|
||||||
|
submit = State()
|
||||||
|
|
||||||
|
class AnswerState(StatesGroup):
|
||||||
|
answer = State()
|
||||||
|
submit = State()
|
||||||
1
doners/Shop-bot/utils/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import db
|
||||||
1
doners/Shop-bot/utils/db/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .storage import DatabaseManager
|
||||||
59
doners/Shop-bot/utils/db/storage.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
import sqlite3 as lite
|
||||||
|
|
||||||
|
class DatabaseManager(object):
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
self.conn = lite.connect(path)
|
||||||
|
self.conn.execute('pragma foreign_keys = on')
|
||||||
|
self.conn.commit()
|
||||||
|
self.cur = self.conn.cursor()
|
||||||
|
|
||||||
|
def create_tables(self):
|
||||||
|
self.query('CREATE TABLE IF NOT EXISTS products (idx text, title text, body text, photo blob, price int, tag text)')
|
||||||
|
self.query('CREATE TABLE IF NOT EXISTS orders (cid int, usr_name text, usr_address text, products text)')
|
||||||
|
self.query('CREATE TABLE IF NOT EXISTS cart (cid int, idx text, quantity int)')
|
||||||
|
self.query('CREATE TABLE IF NOT EXISTS categories (idx text, title text)')
|
||||||
|
self.query('CREATE TABLE IF NOT EXISTS wallet (cid int, balance real)')
|
||||||
|
self.query('CREATE TABLE IF NOT EXISTS questions (cid int, question text)')
|
||||||
|
|
||||||
|
def query(self, arg, values=None):
|
||||||
|
if values == None:
|
||||||
|
self.cur.execute(arg)
|
||||||
|
else:
|
||||||
|
self.cur.execute(arg, values)
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def fetchone(self, arg, values=None):
|
||||||
|
if values == None:
|
||||||
|
self.cur.execute(arg)
|
||||||
|
else:
|
||||||
|
self.cur.execute(arg, values)
|
||||||
|
return self.cur.fetchone()
|
||||||
|
|
||||||
|
def fetchall(self, arg, values=None):
|
||||||
|
if values == None:
|
||||||
|
self.cur.execute(arg)
|
||||||
|
else:
|
||||||
|
self.cur.execute(arg, values)
|
||||||
|
return self.cur.fetchall()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
products: idx text, title text, body text, photo blob, price int, tag text
|
||||||
|
|
||||||
|
orders: cid int, usr_name text, usr_address text, products text
|
||||||
|
|
||||||
|
cart: cid int, idx text, quantity int ==> product_idx
|
||||||
|
|
||||||
|
categories: idx text, title text
|
||||||
|
|
||||||
|
wallet: cid int, balance real
|
||||||
|
|
||||||
|
questions: cid int, question text
|
||||||
|
|
||||||
|
'''
|
||||||
0
doners/__init__.py
Normal file
0
doners/bot-market/__init__.py
Normal file
0
doners/bot-market/app/__init__.py
Normal file
0
doners/bot-market/app/database/__init__.py
Normal file
0
doners/old/__init__.py
Normal file
23
doners/shop-telegram-bot/.dockerignore
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/images
|
||||||
|
data.db
|
||||||
|
*.pyc
|
||||||
|
*.pyd
|
||||||
|
sticker.tgs
|
||||||
|
config.ini
|
||||||
|
src/test.py
|
||||||
|
aws.sh
|
||||||
|
KP.pem
|
||||||
|
ideas.txt
|
||||||
|
/tmp
|
||||||
|
DOCS/overview.md
|
||||||
|
src/modules/
|
||||||
|
backups/
|
||||||
|
logs/
|
||||||
|
DOCS/
|
||||||
|
.github/
|
||||||
|
.git/
|
||||||
|
tests/
|
||||||
|
LICENCE
|
||||||
|
readme.md
|
||||||
|
start.cmd
|
||||||
|
start.sh
|
||||||
2
doners/shop-telegram-bot/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Monero: 43fxouHQFZ5guiyYSUWh6eL1ZQ7pDtVV8D1kKUzp4aYwQBgLjyY8q7KjeEbDvvTYCDPFEdDxz9duBdRrZPnjiSMwVGV4jZj
|
||||||
|
Bitcoin: bc1qnzft2cfty0hqptxjx9ajk4m2q9x3a30gvpylh2
|
||||||
27
doners/shop-telegram-bot/.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Помогите нам стать лучше
|
||||||
|
title: "[BUG]"
|
||||||
|
labels: bug
|
||||||
|
assignees: w1png
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Опишите ошибку**
|
||||||
|
Краткое и понятное описание проблемы.
|
||||||
|
|
||||||
|
**Как воспроизвести ошибку**
|
||||||
|
1. Откройте '...'
|
||||||
|
2. Нажмите на '....'
|
||||||
|
3. Пролистайте до '....'
|
||||||
|
4. Ошибка
|
||||||
|
|
||||||
|
**Ожидаемое поведение**
|
||||||
|
Краткое и понятное описание ожидаемого поведения.
|
||||||
|
|
||||||
|
**Скриншоты**
|
||||||
|
Прикрепите скриншот с проблемой.
|
||||||
|
|
||||||
|
**Информация о системе:**
|
||||||
|
- Операционная система
|
||||||
|
- Версия Python
|
||||||
15
doners/shop-telegram-bot/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/images
|
||||||
|
data.db
|
||||||
|
*.pyc
|
||||||
|
*.pyd
|
||||||
|
sticker.tgs
|
||||||
|
config.ini
|
||||||
|
src/test.py
|
||||||
|
aws.sh
|
||||||
|
KP.pem
|
||||||
|
ideas.txt
|
||||||
|
/tmp
|
||||||
|
DOCS/overview.md
|
||||||
|
src/modules/
|
||||||
|
backups/
|
||||||
|
logs/
|
||||||
BIN
doners/shop-telegram-bot/DOCS/bot_overview.gif
Normal file
|
After Width: | Height: | Size: 11 MiB |
7
doners/shop-telegram-bot/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM python:3.10-slim-buster
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN python3 -m pip install -r requirements.txt
|
||||||
|
CMD python3 installer.py --nointeract && python3 src/main.py
|
||||||
674
doners/shop-telegram-bot/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
0
doners/shop-telegram-bot/__init__.py
Normal file
174
doners/shop-telegram-bot/installer.py
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
from os import system, name, remove, mkdir, rmdir, listdir, environ
|
||||||
|
from os.path import exists
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
def clearConsole():
|
||||||
|
system("cls" if name in ("nt", "dos") else "clear")
|
||||||
|
|
||||||
|
def create_config(token, main_admin_id, config_path="config.ini"):
|
||||||
|
DEFAULT_CONFIG_TEXT = f"""[main_settings]
|
||||||
|
token = {token}
|
||||||
|
mainadminid = {main_admin_id}
|
||||||
|
debug = 0
|
||||||
|
|
||||||
|
[shop_settings]
|
||||||
|
name = Название магазина
|
||||||
|
greeting = Добро пожаловать!
|
||||||
|
refundpolicy = Текст для вкладки "Политика возврата"
|
||||||
|
contacts = Текст для вкладки "Контакты"
|
||||||
|
enableimage = 1
|
||||||
|
enablesticker = 0
|
||||||
|
enablephonenumber = 0
|
||||||
|
enabledelivery = 0
|
||||||
|
delivery_price = 0.0
|
||||||
|
enablecaptcha = 1
|
||||||
|
|
||||||
|
[stats_settings]
|
||||||
|
barcolor = 3299ff
|
||||||
|
borderwidth = 1
|
||||||
|
titlefontsize = 20
|
||||||
|
axisfontsize = 12
|
||||||
|
tickfontsize = 8
|
||||||
|
"""
|
||||||
|
with open(config_path, "w") as config:
|
||||||
|
config.write(DEFAULT_CONFIG_TEXT)
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_CATS_TEXT = """
|
||||||
|
CREATE TABLE "cats" (
|
||||||
|
"id" INTEGER,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
PRIMARY KEY("id")
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
CREATE_ITEMS_TEXT = """
|
||||||
|
CREATE TABLE "items" (
|
||||||
|
"id" INTEGER,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"price" FLOAT NOT NULL,
|
||||||
|
"cat_id" INTEGER NOT NULL,
|
||||||
|
"desc" TEXT,
|
||||||
|
"active" INTEGER,
|
||||||
|
"amount" INTEGER,
|
||||||
|
"image_id" INTEGER,
|
||||||
|
"hide_image" INTEGER,
|
||||||
|
PRIMARY KEY("id")
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
CREATE_ORDERS_TEXT = """
|
||||||
|
CREATE TABLE "orders" (
|
||||||
|
"order_id" INTEGER,
|
||||||
|
"user_id" INTEGER,
|
||||||
|
"item_list" TEXT,
|
||||||
|
"email_adress" TEXT,
|
||||||
|
"phone_number" TEXT,
|
||||||
|
"home_adress" TEXT,
|
||||||
|
"additional_message" TEXT,
|
||||||
|
"date" TEXT,
|
||||||
|
"status" INTEGER
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
CREATE_USERS_TEXT = """
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"user_id" INTEGER NOT NULL,
|
||||||
|
"is_admin" INTEGER,
|
||||||
|
"is_manager" INTEGER,
|
||||||
|
"notification" INTEGER,
|
||||||
|
"date_created" TEXT,
|
||||||
|
"cart" TEXT,
|
||||||
|
"cart_delivery" INTEGER
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
CREATE_COMMANDS_TEXT = """
|
||||||
|
CREATE TABLE "commands" (
|
||||||
|
"id" INTEGER NOT NULL,
|
||||||
|
"command" TEXT,
|
||||||
|
"response" TEXT,
|
||||||
|
PRIMARY KEY("id")
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_db():
|
||||||
|
conn = sqlite3.connect("data.db")
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute(CREATE_CATS_TEXT)
|
||||||
|
c.execute(CREATE_ITEMS_TEXT)
|
||||||
|
c.execute(CREATE_ORDERS_TEXT)
|
||||||
|
c.execute(CREATE_USERS_TEXT)
|
||||||
|
c.execute(CREATE_COMMANDS_TEXT)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def do_files_exist():
|
||||||
|
return any(list(map(exists, ["config.ini", "images", "data.db"])))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if "--nointeract" in argv:
|
||||||
|
if do_files_exist():
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
token = environ.get("TELEGRAM_TOKEN")
|
||||||
|
main_admin_id = environ.get("MAIN_ADMIN_ID")
|
||||||
|
if token is None or main_admin_id is None:
|
||||||
|
print("Не указаны переменные окружения TELEGRAM_TOKEN или MAIN_ADMIN_ID")
|
||||||
|
exit(1)
|
||||||
|
create_config(token, main_admin_id)
|
||||||
|
|
||||||
|
create_db()
|
||||||
|
[mkdir(name) for name in ["backups", "images"]]
|
||||||
|
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
clearConsole()
|
||||||
|
if do_files_exist():
|
||||||
|
while True:
|
||||||
|
confirmation = input("Вы уверены, что хотите повторно запустить процесс установки? Все данные будут утеряны! (y/N) ")
|
||||||
|
if confirmation.lower() in ["y", "yes", "n", "no", ""]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
confirmation = "y"
|
||||||
|
|
||||||
|
|
||||||
|
if confirmation.lower() in ["y", "yes"]:
|
||||||
|
print("Вы можете узнать как получить токен бота, перейдя по ссылке: https://youtu.be/fyISLEvzIec")
|
||||||
|
token = input("Введите токен бота: ")
|
||||||
|
print("Вы можете получить ваш ID, написав \"/start\" боту @userinfobot")
|
||||||
|
main_admin_id = input("Введите ID главного администратора: ")
|
||||||
|
if main_admin_id.isalnum():
|
||||||
|
if exists("data.db"):
|
||||||
|
remove("data.db")
|
||||||
|
print("База данных была удалена.")
|
||||||
|
create_db()
|
||||||
|
print("База данных была создана.")
|
||||||
|
if exists("config.ini"):
|
||||||
|
remove("config.ini")
|
||||||
|
print("Файл настроек был удален.")
|
||||||
|
create_config(token, main_admin_id)
|
||||||
|
print("Файл настроек был создан.")
|
||||||
|
if exists("images"):
|
||||||
|
for file in listdir("images"):
|
||||||
|
remove("images/" + file)
|
||||||
|
rmdir("images")
|
||||||
|
print("Папка \"images\" была удалена.")
|
||||||
|
mkdir("images")
|
||||||
|
print("Папка \"images\" была создана.")
|
||||||
|
if exists("backups"):
|
||||||
|
for folder in listdir("backups"):
|
||||||
|
for file in listdir("backups/" + folder):
|
||||||
|
remove(f"backups/{folder}/{file}")
|
||||||
|
rmdir(f"backups/{folder}")
|
||||||
|
rmdir("backups")
|
||||||
|
print("Папка \"backups\" была удалена.")
|
||||||
|
mkdir("backups")
|
||||||
|
print("Папка \"backups\" была создана.")
|
||||||
|
else:
|
||||||
|
print("Неверный ID главного администратора.")
|
||||||
|
else:
|
||||||
|
print("Установка была отменена.")
|
||||||
|
|
||||||
|
|
||||||
|
input("Нажмите ENTER, чтобы продолжить...")
|
||||||
83
doners/shop-telegram-bot/readme.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# ⚠️ Эта версия бота больше не обновляется!
|
||||||
|
Сейчас я переписываю бота, за обновлениями следите на ветке rewrite :)
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## 🪲 В случае возникновения ошибок открывайте тикет во вкладке Issues :)
|
||||||
|
|
||||||
|
# Навигация
|
||||||
|
|
||||||
|
- [Навигация](#навигация)
|
||||||
|
- [Зачем нужен этот бот?](#зачем-нужен-этот-бот)
|
||||||
|
- [Установка](#установка)
|
||||||
|
- [Docker](#docker)
|
||||||
|
- [Python](#python)
|
||||||
|
- [Установка необходимых Python-пакетов](#установка-необходимых-python-пакетов)
|
||||||
|
- [Запуск установщика](#запуск-установщика)
|
||||||
|
- [Запуск бота](#запуск-бота)
|
||||||
|
- [Запуск с помощью скрипта](#запуск-с-помощью-скрипта)
|
||||||
|
- [Linux](#linux)
|
||||||
|
- [Macos](#macos)
|
||||||
|
- [Запуск вручную](#запуск-вручную)
|
||||||
|
- [Режим отладки](#режим-отладки)
|
||||||
|
|
||||||
|
# Зачем нужен этот бот?
|
||||||
|
|
||||||
|
Зачастую люди, желающие открыть маленький интернет-бизнес, делают это с помощью профиля в социальных сетях, что требует вручную обрабатывать каждую заявку. Этот бот позволит каждому быстро открыть автоматизированный магазин на базе телеграм бота, что значительно уменьшит время обработки заказов.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Установка
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
Этот способ установки является основным и рекомендуемым. Он позволяет установить бота в репродуцируемом окружении, что упрощает его обновление и установку на сервер.
|
||||||
|
|
||||||
|
Для работы бота необходимо установить [Docker](https://docs.docker.com/get-docker/) и выполнить следущую команду:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d \
|
||||||
|
-e MAIN_ADMIN_ID='Ваш Telegram ID' \
|
||||||
|
-e TELEGRAM_TOKEN='token' \
|
||||||
|
w1png/shop-telegram-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
Для работы бота необходимо установить [Python версии 3.10 и выше](https://www.python.org/downloads/).
|
||||||
|
|
||||||
|
### Установка необходимых Python-пакетов
|
||||||
|
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
### Запуск установщика
|
||||||
|
|
||||||
|
Перед запуском установщика требуется [создать токен](https://youtu.be/fyISLEvzIec) для телеграм бота и [получить ваш ID](https://badcode.ru/kak-v-telegram-uznat-svoi-id/).
|
||||||
|
|
||||||
|
Установщик запускается с помощью команды:
|
||||||
|
|
||||||
|
python3 installer.py
|
||||||
|
|
||||||
|
## Запуск бота
|
||||||
|
|
||||||
|
### Запуск с помощью скрипта
|
||||||
|
|
||||||
|
#### Linux/MacOS
|
||||||
|
|
||||||
|
$ chmod +x start.sh
|
||||||
|
$ ./start.sh
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
$ start.cmd
|
||||||
|
|
||||||
|
### Запуск вручную
|
||||||
|
|
||||||
|
python3 main.py
|
||||||
|
|
||||||
|
# Режим отладки
|
||||||
|
|
||||||
|
Режим отладки можно активировать во вкладке "Основные настройки".
|
||||||
|
После активации в терминале начнут отображаться все сообщения и вызовы в формате:
|
||||||
|
|
||||||
|
DEBUG: <MESSAGE/CALL> [<user_id>] <Сообщение/вызов>
|
||||||
|
|
||||||
|
*Пример: `DEBUG CALL [462741] admin_itemManagement`*
|
||||||
6
doners/shop-telegram-bot/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
aiogram==2.19
|
||||||
|
captcha==0.3
|
||||||
|
matplotlib==3.5.1
|
||||||
|
phonenumbers==8.12.40
|
||||||
|
pymorphy2==0.9.1
|
||||||
|
pyparsing==3.0.0
|
||||||
0
doners/shop-telegram-bot/src/__init__.py
Normal file
51
doners/shop-telegram-bot/src/category.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import sqlite3
|
||||||
|
import item as itm
|
||||||
|
|
||||||
|
conn = sqlite3.connect("data.db")
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
class Category:
|
||||||
|
def __init__(self, cat_id):
|
||||||
|
self.id = cat_id
|
||||||
|
|
||||||
|
def __eq__(self, __o: object) -> bool:
|
||||||
|
return self.get_id() == __o.get_id()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.get_name()
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def __clist(self):
|
||||||
|
c.execute(f"SELECT * FROM cats WHERE id=?", [self.get_id()])
|
||||||
|
return list(c)[0]
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.__clist()[1]
|
||||||
|
|
||||||
|
def set_name(self, value):
|
||||||
|
c.execute(f"UPDATE cats SET name=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
c.execute(f"DELETE FROM cats WHERE id=?", [self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_item_list(self):
|
||||||
|
c.execute(f"SELECT * FROM items WHERE cat_id=?", [self.get_id()])
|
||||||
|
return list(map(itm.Item, [item[0] for item in list(c)]))
|
||||||
|
|
||||||
|
|
||||||
|
def get_cat_list():
|
||||||
|
c.execute(f"SELECT * FROM cats")
|
||||||
|
return list(map(Category, [cat[0] for cat in list(c)]))
|
||||||
|
|
||||||
|
|
||||||
|
def create_cat(cat_name):
|
||||||
|
c.execute(f"INSERT INTO cats(name) VALUES(?)", [cat_name])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(get_cat_list())
|
||||||
61
doners/shop-telegram-bot/src/commands.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import sqlite3
|
||||||
|
|
||||||
|
conn = sqlite3.connect("data.db")
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
|
||||||
|
def get_commands():
|
||||||
|
return list(map(Command, [command[0] for command in list(c.execute("SELECT * FROM commands"))]))
|
||||||
|
|
||||||
|
def does_command_exist(command_id=None, command=None):
|
||||||
|
result = False
|
||||||
|
if command_id:
|
||||||
|
c.execute("SELECT * FROM commands WHERE id=?", [command_id])
|
||||||
|
result = len(list(c)) >= 1
|
||||||
|
elif command:
|
||||||
|
c.execute("SELECT * FROM commands WHERE command=?", [command])
|
||||||
|
result = len(list(c)) >= 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_command_by_command(command):
|
||||||
|
c.execute("SELECT * FROM commands WHERE command=?", [command])
|
||||||
|
return Command(list(c)[0][0])
|
||||||
|
|
||||||
|
|
||||||
|
def create_command(command, response):
|
||||||
|
c.execute("INSERT INTO commands(command, response) VALUES(?,?)", [command, response])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: rework the permission system and implement command permissions
|
||||||
|
class Command:
|
||||||
|
def __init__(self, command_id=None):
|
||||||
|
self.command_id = command_id
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"[{self.get_id()}] {self.get_command()}"
|
||||||
|
|
||||||
|
def __clist(self):
|
||||||
|
c.execute("SELECT * FROM commands WHERE id=?", [self.command_id])
|
||||||
|
return list(c)[0]
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.command_id
|
||||||
|
|
||||||
|
def get_command(self):
|
||||||
|
return self.__clist()[1]
|
||||||
|
|
||||||
|
def get_response(self):
|
||||||
|
return self.__clist()[2]
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
c.execute("DELETE FROM commands WHERE id=?", [self.command_id])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# create_command("test", "response")
|
||||||
|
|
||||||
|
for command in get_commands():
|
||||||
|
print(command)
|
||||||
111
doners/shop-telegram-bot/src/item.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import sqlite3
|
||||||
|
from settings import Settings
|
||||||
|
from os.path import exists
|
||||||
|
from doners.old_2.main import notify_admins
|
||||||
|
|
||||||
|
conn = sqlite3.connect("data.db")
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
def does_item_exist(item_id):
|
||||||
|
c.execute(f"SELECT * FROM items WHERE id=?", [item_id])
|
||||||
|
return len(list(c)) == 1
|
||||||
|
|
||||||
|
|
||||||
|
class Item:
|
||||||
|
def __init__(self, item_id):
|
||||||
|
self.__item_id = item_id
|
||||||
|
|
||||||
|
def __eq__(self, __o: object):
|
||||||
|
return self.get_id() == __o.get_id()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"[{self.get_id()}] {self.get_name()}"
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.__item_id
|
||||||
|
|
||||||
|
def __clist(self):
|
||||||
|
c.execute(f"SELECT * FROM items WHERE id=?", [self.get_id()])
|
||||||
|
return list(c)[0]
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.__clist()[1]
|
||||||
|
|
||||||
|
def set_name(self, value):
|
||||||
|
c.execute(f"UPDATE items SET name=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_price(self):
|
||||||
|
return self.__clist()[2]
|
||||||
|
|
||||||
|
def set_price(self, value):
|
||||||
|
c.execute(f"UPDATE items SET price=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# TODO: change to get_cat and change to cat.get_id() everywhere
|
||||||
|
def get_cat_id(self):
|
||||||
|
return self.__clist()[3]
|
||||||
|
|
||||||
|
def set_cat_id(self, value):
|
||||||
|
c.execute(f"UPDATE items SET cat_id=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_desc(self):
|
||||||
|
return self.__clist()[4]
|
||||||
|
|
||||||
|
def set_desc(self, value):
|
||||||
|
c.execute(f"UPDATE items SET desc=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
return self.__clist()[5] == 1
|
||||||
|
|
||||||
|
def set_active(self, value):
|
||||||
|
c.execute(f"UPDATE items SET active=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_amount(self):
|
||||||
|
return int(self.__clist()[6])
|
||||||
|
|
||||||
|
def set_amount(self, value):
|
||||||
|
c.execute(f"UPDATE items SET amount=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_image_id(self):
|
||||||
|
return self.__clist()[7]
|
||||||
|
|
||||||
|
def get_image(self):
|
||||||
|
return open("images/" + self.get_image_id(), "rb")
|
||||||
|
|
||||||
|
def set_image_id(self, value):
|
||||||
|
c.execute(f"UPDATE items SET image_id=? WHERE id=?", [value, self.get_id()])
|
||||||
|
|
||||||
|
async def is_hide_image(self):
|
||||||
|
if exists(f"images/{self.get_image_id()}"):
|
||||||
|
return self.__clist()[8] == 1
|
||||||
|
if self.get_image_id() != "None" and settings.is_debug():
|
||||||
|
await notify_admins(f"Файла \"images/{self.get_image_id()}\" для товара \"{self.get_name()}\" не существует!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_hide_image(self, value):
|
||||||
|
c.execute("UPDATE items SET hide_image=? WHERE id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
c.execute(f"DELETE FROM items WHERE id=?", [self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def get_item_list():
|
||||||
|
c.execute("SELECT * FROM items")
|
||||||
|
return list(map(Item, [item[0] for item in list(c)]))
|
||||||
|
|
||||||
|
|
||||||
|
def create_item(name, price, cat_id, desc, image_id="None", active=True):
|
||||||
|
c.execute(f"INSERT INTO items(name, price, cat_id, desc, active, amount, image_id, hide_image) VALUES(?, ?, ?, ?, ?, 0, ?, 0)", [name, price, cat_id, desc, 1 if active else 0, image_id])
|
||||||
|
conn.commit()
|
||||||
|
return get_item_list()[-1]
|
||||||
|
|
||||||
|
|
||||||
2522
doners/shop-telegram-bot/src/main.py
Normal file
477
doners/shop-telegram-bot/src/markups.py
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
from os import listdir
|
||||||
|
from aiogram import types
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import text_templates as tt
|
||||||
|
from settings import Settings
|
||||||
|
import commands
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
# Back buttons
|
||||||
|
# Misc
|
||||||
|
btnBackAdmin = types.InlineKeyboardButton(text=tt.back, callback_data="admin_adminPanel")
|
||||||
|
|
||||||
|
# Item management
|
||||||
|
btnBackItemManagement = types.InlineKeyboardButton(text=tt.back, callback_data="admin_itemManagement")
|
||||||
|
btnBackEditCatChooseCategory = types.InlineKeyboardButton(text=tt.back, callback_data="admin_editCatChooseCategory")
|
||||||
|
def btnBackEditCat(cat_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_editCat{cat_id}")
|
||||||
|
btnBackEditItemChooseCategory = types.InlineKeyboardButton(text=tt.back, callback_data="admin_editItemChooseCategory")
|
||||||
|
def btnBackEditItemChooseItem(cat_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_editItemChooseItem{cat_id}")
|
||||||
|
def btnBackEditItem(item_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_editItem{item_id}")
|
||||||
|
|
||||||
|
# User management
|
||||||
|
btnBackUserManagement = types.InlineKeyboardButton(text=tt.back, callback_data="admin_userManagement")
|
||||||
|
def btnBackSeeUserProfile(user_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_seeUserProfile{user_id}")
|
||||||
|
def btnBackSeeUserOrders(user_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_seeUserOrders{user_id}")
|
||||||
|
|
||||||
|
# Stats
|
||||||
|
btnBackShopStats = types.InlineKeyboardButton(text=tt.back, callback_data="admin_shopStats")
|
||||||
|
btnBackRegistratonStats = types.InlineKeyboardButton(text=tt.back, callback_data="admin_registrationStatsBack")
|
||||||
|
btnBackOrderStats = types.InlineKeyboardButton(text=tt.back, callback_data="admin_orderStatsBack")
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
btnBackShopSettingsDel = types.InlineKeyboardButton(text=tt.back, callback_data="admin_shopSettingsDel")
|
||||||
|
btnBackShopSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_shopSettings")
|
||||||
|
btnBackStatsSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_statsSettings")
|
||||||
|
btnBackMainSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_mainSettings")
|
||||||
|
btnBackCheckoutSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_checkoutSettings")
|
||||||
|
btnBackAdditionalSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_additionalSettings")
|
||||||
|
btnBackCustomCommands = types.InlineKeyboardButton(text=tt.back, callback_data="admin_customCommands")
|
||||||
|
btnBackSystemSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_systemSettings")
|
||||||
|
btnBackItemSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_itemSettings")
|
||||||
|
btnBackBackups = types.InlineKeyboardButton(text=tt.back, callback_data="admin_backups")
|
||||||
|
|
||||||
|
# /start menu
|
||||||
|
btnBackFaq = types.InlineKeyboardButton(text=tt.back, callback_data="faq")
|
||||||
|
btnBackProfile = types.InlineKeyboardButton(text=tt.back, callback_data="profile")
|
||||||
|
btnBackMyOrder = types.InlineKeyboardButton(text=tt.back, callback_data="myOrders")
|
||||||
|
btnBackCatalogue = types.InlineKeyboardButton(text=tt.back, callback_data="catalogue")
|
||||||
|
def btnBackViewCat(cat_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"viewCat{cat_id}")
|
||||||
|
def btnBackViewItem(item_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"viewItem{item_id}")
|
||||||
|
btnBackCart = types.InlineKeyboardButton(text=tt.back, callback_data="cart")
|
||||||
|
btnBackCartDel = types.InlineKeyboardButton(text=tt.back, callback_data="cartDel")
|
||||||
|
btnBackOrders = types.InlineKeyboardButton(text=tt.back, callback_data="manager_orders")
|
||||||
|
|
||||||
|
# Single buttons
|
||||||
|
btnAdminPanel = types.KeyboardButton(tt.admin_panel)
|
||||||
|
btnOrders = types.KeyboardButton(tt.orders)
|
||||||
|
|
||||||
|
def single_button(btn):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(btn)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
|
||||||
|
# Markups
|
||||||
|
# /start buttons
|
||||||
|
def get_markup_main():
|
||||||
|
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
markup.add(types.KeyboardButton(tt.catalogue))
|
||||||
|
markup.add(types.KeyboardButton(tt.cart))
|
||||||
|
markup.add(types.KeyboardButton(tt.profile), types.KeyboardButton(tt.faq))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_admin():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.item_management, callback_data="admin_itemManagement"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.user_management, callback_data="admin_userManagement"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.shop_stats, callback_data="admin_shopStats"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.bot_settings, callback_data="admin_shopSettings"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_faq():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.contacts, callback_data="contacts"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.refund, callback_data="refund"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_profile(user):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.my_orders, callback_data="myOrders"))
|
||||||
|
# markup.add(types.InlineKeyboardButton(text=tt.my_support_tickets, callback_data="mySupportTickets"))
|
||||||
|
if user.is_admin():
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_notif if user.notif_on() else tt.enable_notif, callback_data="changeEnableNotif"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_myOrders(order_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for order in order_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=order.get_order_id(), callback_data=f"viewMyOrder{order.get_order_id()}"))
|
||||||
|
markup.add(btnBackProfile)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_viewMyOrder(order):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
match order.get_status():
|
||||||
|
case 0:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.cancel_order, callback_data=f"cancelOrder{order.get_order_id()}"))
|
||||||
|
case -1:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.restore_order, callback_data=f"restoreOrder{order.get_order_id()}"))
|
||||||
|
markup.add(btnBackMyOrder)
|
||||||
|
return markup
|
||||||
|
def get_markup_cart(user):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
delivery_price = '{:.2f}'.format(float(settings.get_delivery_price()))
|
||||||
|
for item_and_amount in user.get_cart_amount():
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"{item_and_amount[0].get_name()[:30-len(f' - {item_and_amount[1]}шт.')-3]}... - {item_and_amount[1]}шт.", callback_data=f"viewItem{item_and_amount[0].get_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"{item_and_amount[0].get_price() * item_and_amount[1]}руб.", callback_data="None"), types.InlineKeyboardButton(text=tt.plus, callback_data=f"addToCartFromCart{item_and_amount[0].get_id()}"), types.InlineKeyboardButton(text=tt.minus, callback_data=f"removeFromCartFromCart{item_and_amount[0].get_id()}"))
|
||||||
|
if settings.is_delivery_enabled():
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delivery_on(delivery_price) if user.is_cart_delivery() else tt.delivery_off(delivery_price), callback_data="changeCartDelivery"))
|
||||||
|
else:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.pickup, callback_data="None"))
|
||||||
|
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.clear_cart, callback_data="clearCart"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"Всего: {'{:.2f}'.format(user.get_cart_price())}руб.", callback_data="None"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.cart_checkout, callback_data="checkoutCart"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_captcha():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text="Новая CAPTCHA", callback_data="refreshCaptcha"))
|
||||||
|
markup.add(btnBackCartDel)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_checkoutCartConfirmation():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.confirm, callback_data=f"checkoutCartConfirm"), types.InlineKeyboardButton(text=tt.deny, callback_data="cart"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# Catalogue
|
||||||
|
def get_markup_catalogue(cat_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for cat in cat_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=cat.get_name(), callback_data=f"viewCat{cat.get_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.search, callback_data="search"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_search(query):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for item in query:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=item.get_name(), callback_data=f"viewItem{item.get_id()}"))
|
||||||
|
markup.add(btnBackCatalogue)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
|
||||||
|
def get_markup_viewCat(item_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for item in item_list:
|
||||||
|
if item.is_active():
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"{item.get_name()} - {item.get_price()} руб.", callback_data=f"viewItem{item.get_id()}"))
|
||||||
|
markup.add(btnBackCatalogue)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_viewItem(item):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.add_to_cart, callback_data=f"addToCart{item.get_id()}"))
|
||||||
|
markup.add(btnBackViewCat(item.get_cat_id()))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# Admin panel tabs
|
||||||
|
# Item management
|
||||||
|
def get_markup_itemManagement():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.add_cat, callback_data="admin_addCat"), types.InlineKeyboardButton(text=tt.add_item, callback_data="admin_addItem"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.edit_cat, callback_data="admin_editCatChooseCategory"), types.InlineKeyboardButton(text=tt.edit_item, callback_data="admin_editItemChooseCategory"))
|
||||||
|
markup.add(btnBackAdmin)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_editCatChooseCategory(cat_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for cat in cat_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_editCat{cat.get_id()}"))
|
||||||
|
markup.add(btnBackItemManagement)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_editCat(cat_id):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_name, callback_data=f"admin_editCatName{cat_id}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data=f"admin_editCatDelete{cat_id}"))
|
||||||
|
markup.add(btnBackEditCatChooseCategory)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_addItemSetCat(cat_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for cat in cat_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_addItemSetCat{cat.get_id()}"))
|
||||||
|
markup.add(btnBackItemManagement)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
btnSkipAddItemSetImage = types.InlineKeyboardButton(text=tt.skip, callback_data="admin_skipSetAddItemSetImage")
|
||||||
|
|
||||||
|
def get_markup_addItemConfirmation():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.confirm, callback_data="admin_addItemConfirm"), types.InlineKeyboardButton(text=tt.deny, callback_data="admin_itemManagement"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_editItemChooseCategory(cat_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for cat in cat_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_editItemChooseItem{cat.get_id()}"))
|
||||||
|
markup.add(btnBackItemManagement)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_editItemChooseItem(item_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for item in item_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{item.get_id()}] {item.get_name()}", callback_data=f"admin_editItem{item.get_id()}"))
|
||||||
|
markup.add(btnBackEditItemChooseCategory)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
async def get_markup_editItem(item):
|
||||||
|
itemid = item.get_id()
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_name, callback_data=f"admin_editItemName{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_image, callback_data=f"admin_editItemImage{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.show_image if await item.is_hide_image() else tt.hide_image, callback_data=f"admin_editItemHideImage{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_desc, callback_data=f"admin_editItemDesc{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_price, callback_data=f"admin_editItemPrice{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_item_cat, callback_data=f"admin_editItemCat{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_stock, callback_data=f"admin_editItemStock{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=(tt.hide if item.is_active() else tt.show), callback_data=f"admin_editItemHide{itemid}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data=f"admin_editItemDelete{itemid}"))
|
||||||
|
markup.add(btnBackEditItemChooseItem(item.get_cat_id()))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_editItemCat(item_id, cat_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for cat in cat_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_editItemSetCat{cat.get_id()}"))
|
||||||
|
markup.add(btnBackEditItem(item_id))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# User management
|
||||||
|
def get_markup_userManagement():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.user_profile, callback_data="admin_seeUserProfile"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.notify_everyone, callback_data="admin_notifyEveryone"))
|
||||||
|
markup.add(btnBackAdmin)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_notifyEveryoneConfirmation():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.confirm, callback_data="admin_notifyEveryoneConfirm"), btnBackUserManagement)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_seeUserProfile(user):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.orders, callback_data=f"admin_seeUserOrders{user.get_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.remove_manager_role if user.is_manager() else tt.add_manager_role, callback_data=f"admin_changeUserManager{user.get_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.remove_admin_role if user.is_admin() else tt.add_admin_role, callback_data=f"admin_changeUserAdmin{user.get_id()}"))
|
||||||
|
markup.add(btnBackUserManagement)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_seeUserOrders(user):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for order in user.get_orders():
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{order.get_order_id()}]", callback_data=f"admin_seeUserOrder{order.get_order_id()}"))
|
||||||
|
markup.add(btnBackSeeUserProfile(user.get_id()))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_seeUserOrder(order):
|
||||||
|
markup = markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.processing), callback_data=f"admin_changeOrderStatusProcessing{order.get_order_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.delivery), callback_data=f"admin_changeOrderStatusDelivery{order.get_order_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.done), callback_data=f"admin_changeOrderStatusDone{order.get_order_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.cancelled), callback_data=f"admin_changeOrderStatusCancel{order.get_order_id()}"))
|
||||||
|
markup.add(btnBackSeeUserOrders(order.get_user_id()))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# Shop stats
|
||||||
|
def get_markup_shopStats():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.registration_stats, callback_data="admin_registrationStats"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.order_stats, callback_data="admin_orderStats"))
|
||||||
|
markup.add(btnBackAdmin)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_registrationStats():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.all_time, callback_data="admin_registrationStatsAllTime"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.monthly, callback_data="admin_registrationStatsMonthly"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.weekly, callback_data="admin_registrationStatsWeekly"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.daily, callback_data="admin_registrationStatsDaily"))
|
||||||
|
markup.add(btnBackShopStats)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_orderStats():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.all_time, callback_data="admin_orderStatsAllTime"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.monthly, callback_data="admin_orderStatsMonthly"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.weekly, callback_data="admin_orderStatsWeekly"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.daily, callback_data="admin_orderStatsDaily"))
|
||||||
|
markup.add(btnBackShopStats)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# Shop settings
|
||||||
|
def get_markup_shopSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.main_settings, callback_data="admin_mainSettings"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.item_settings, callback_data="admin_itemSettings"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.checkout_settings, callback_data="admin_checkoutSettings"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.stats_settings, callback_data="admin_statsSettings"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.additional_settings, callback_data="admin_additionalSettings"))
|
||||||
|
markup.add(btnBackAdmin)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_mainSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"Название: {settings.get_shop_name()}", callback_data="admin_changeShopName"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"Приветствие: {settings.get_shop_greeting()}", callback_data="admin_changeShopGreeting"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"Политика возврата: {settings.get_refund_policy()}", callback_data="admin_changeShopRefundPolicy"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"Контакты: {settings.get_shop_contacts()}", callback_data="admin_changeShopContacts"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_sticker if settings.is_sticker_enabled() else tt.enable_sticker, callback_data="admin_changeEnableSticker"))
|
||||||
|
markup.add(btnBackShopSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_itemSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_item_image if settings.is_item_image_enabled() else tt.enable_item_image, callback_data="admin_changeEnableItemImage"))
|
||||||
|
markup.add(btnBackShopSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_checkoutSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delivery_price('{:.2f}'.format(settings.get_delivery_price())), callback_data="admin_changeDeliveryPrice"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_delivery if settings.is_delivery_enabled() else tt.enable_delivery, callback_data="admin_changeEnableDelivery"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_phone_number if settings.is_phone_number_enabled() else tt.enable_phone_number, callback_data="admin_changeEnablePhoneNumber"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_captcha if settings.is_captcha_enabled() else tt.enable_captcha, callback_data="admin_changeEnableCaptcha"))
|
||||||
|
markup.add(btnBackShopSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_statsSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.graph_color, callback_data="admin_statsSettingsColor"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.border_width, callback_data="admin_statsSettingsBorderWidth"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.title_font_size, callback_data="admin_statsSettingsTitleFontSize"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.axis_font_size, callback_data="admin_statsSettingsAxisFontSize"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.tick_font_size, callback_data="admin_statsSettingsTickFontSize"))
|
||||||
|
markup.add(btnBackShopSettingsDel)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_statsSettingsColor():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text="⬛️", callback_data="admin_statsSettingsColorBlack"), types.InlineKeyboardButton(text="⬜️", callback_data="admin_statsSettingsColorWhite"), types.InlineKeyboardButton(text="🟥", callback_data="admin_statsSettingsColorRed"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text="🟨", callback_data="admin_statsSettingsColorYellow"), types.InlineKeyboardButton(text="🟪", callback_data="admin_statsSettingsColorPurple"), types.InlineKeyboardButton(text="🟦", callback_data="admin_statsSettingsColorBlue"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text="🟧", callback_data="admin_statsSettingsColorOrange"), types.InlineKeyboardButton(text="🟩", callback_data="admin_statsSettingsColorGreen"), types.InlineKeyboardButton(text="🟫", callback_data="admin_statsSettingsColorBrown"))
|
||||||
|
markup.add(btnBackStatsSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_statsSettingsBorderWidth():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_borderwidth() == "0" else tt.minus, callback_data="None" if settings.get_borderwidth() == "0" else "admin_statsSettingsBorderWidthReduce"), types.InlineKeyboardButton(text=settings.get_borderwidth(), callback_data="admin_statsSettingsBorderWidthDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsBorderWidthAdd"))
|
||||||
|
markup.add(btnBackStatsSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_statsSettingsTitleFontSize():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_titlefontsize() == "2" else tt.minus, callback_data="None" if settings.get_titlefontsize() == "2" else "admin_statsSettingsTitleFontSizeReduce"), types.InlineKeyboardButton(text=settings.get_titlefontsize(), callback_data="admin_statsSettingsTitleFontSizeDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsTitleFontSizeAdd"))
|
||||||
|
markup.add(btnBackStatsSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_statsSettingsAxisFontSize():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_axisfontsize() == "2" else tt.minus, callback_data="None" if settings.get_axisfontsize() == "2" else "admin_statsSettingsAxisFontSizeReduce"), types.InlineKeyboardButton(text=settings.get_axisfontsize(), callback_data="admin_statsSettingsAxisFontSizeDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsAxisFontSizeAdd"))
|
||||||
|
markup.add(btnBackStatsSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_statsSettingsTickFontSize():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_tickfontsize() == "2" else tt.minus, callback_data="None" if settings.get_tickfontsize() == "2" else "admin_statsSettingsTickFontSizeReduce"), types.InlineKeyboardButton(text=settings.get_tickfontsize(), callback_data="admin_statsSettingsTickFontSizeDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsTickFontSizeAdd"))
|
||||||
|
markup.add(btnBackStatsSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_systemSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.clean_images, callback_data="admin_cleanImagesMenu"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.reset_settings, callback_data="admin_resetSettingsMenu"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.clean_database, callback_data="admin_cleanDatabaseMenu"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.backups, callback_data="admin_backups"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.disable_debug if settings.is_debug() else tt.enable_debug, callback_data="admin_changeEnableDebug"))
|
||||||
|
markup.add(btnBackAdditionalSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_backups():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.update_backup, callback_data="admin_updateBackup"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.load_backup, callback_data="admin_loadBackupMenu"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.clean_backups, callback_data="admin_cleanBackupsMenu"))
|
||||||
|
markup.add(btnBackSystemSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_loadBackupMenu():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
backups = listdir("backups")
|
||||||
|
backups.sort(key=lambda b: datetime.strptime(b, "%d-%m-%Y"))
|
||||||
|
for backup in backups[:90]:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=backup, callback_data=f"admin_loadBackup{backup}"))
|
||||||
|
markup.add(btnBackBackups)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_cleanBackupsMenu():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for days in ["7", "30", "90"]:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"{days} дней", callback_data=f"admin_cleanBackups{days}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text="Удалить все резервные копии", callback_data="admin_cleanBackupsAll"))
|
||||||
|
markup.add(btnBackBackups)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_cleanImagesMenu():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data="admin_cleanImages"))
|
||||||
|
markup.add(btnBackSystemSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_resetSettingsMenu():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.reset, callback_data="admin_resetSettings"))
|
||||||
|
markup.add(btnBackSystemSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_cleanDatabaseMenu():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data="admin_cleanDatabase"))
|
||||||
|
markup.add(btnBackSystemSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# Manager tab
|
||||||
|
def get_markup_seeOrder(order, user_id=None):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.processing), callback_data=f"manager_changeOrderStatusProcessing{order.get_order_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.delivery), callback_data=f"manager_changeOrderStatusDelivery{order.get_order_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.done), callback_data=f"manager_changeOrderStatusDone{order.get_order_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.cancelled), callback_data=f"manager_changeOrderStatusCancel{order.get_order_id()}"))
|
||||||
|
markup.add(btnBackSeeUserOrders(user_id) if user_id else btnBackOrders)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_orders():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.processing, callback_data="manager_ordersProcessing"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.delivery, callback_data="manager_ordersDelivery"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.done, callback_data="manager_ordersDone"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.cancelled, callback_data="manager_ordersCancelled"))
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_ordersByOrderList(order_list):
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for order in order_list:
|
||||||
|
markup.add(types.InlineKeyboardButton(text=f"[{order.get_order_id()}]", callback_data=f"manager_seeOrder{order.get_order_id()}"))
|
||||||
|
markup.add(btnBackOrders)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def get_markup_additionalSettings():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.custom_commands, callback_data="admin_customCommands"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.system_settings, callback_data="admin_systemSettings"))
|
||||||
|
markup.add(btnBackShopSettings)
|
||||||
|
return markup
|
||||||
|
|
||||||
|
# Custom commands
|
||||||
|
def get_markup_customCommands():
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for command in commands.get_commands():
|
||||||
|
markup.add(types.InlineKeyboardButton(text=command.get_command(), callback_data="None"), types.InlineKeyboardButton(text=tt.delete, callback_data=f"admin_deleteCommand{command.get_id()}"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.line_separator, callback_data="None"))
|
||||||
|
markup.add(types.InlineKeyboardButton(text=tt.add_command, callback_data="admin_addCommand"))
|
||||||
|
markup.add(btnBackAdditionalSettings)
|
||||||
|
return markup
|
||||||
108
doners/shop-telegram-bot/src/order.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import sqlite3
|
||||||
|
from datetime import datetime
|
||||||
|
import item as itm
|
||||||
|
import text_templates as tt
|
||||||
|
from settings import Settings
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
conn = sqlite3.connect("data.db")
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
class Order:
|
||||||
|
def __init__(self, id):
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.get_order_id()}"
|
||||||
|
|
||||||
|
def get_order_id(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def __clist(self):
|
||||||
|
c.execute(f"SELECT * FROM orders WHERE order_id=?", [self.get_order_id()])
|
||||||
|
return list(c)[0]
|
||||||
|
|
||||||
|
def get_user_id(self):
|
||||||
|
return self.__clist()[1]
|
||||||
|
|
||||||
|
def get_item_list_raw(self):
|
||||||
|
return self.__clist()[2]
|
||||||
|
|
||||||
|
def get_item_list(self):
|
||||||
|
return list(map(itm.Item, [item_id for item_id in self.get_item_list_raw().split(",")]))
|
||||||
|
|
||||||
|
def get_item_list_amount(self):
|
||||||
|
cart = [item.get_id() for item in self.get_item_list()]
|
||||||
|
return [[itm.Item(item_id), cart.count(item_id)] for item_id in set(cart)]
|
||||||
|
|
||||||
|
def get_item_list_price(self):
|
||||||
|
return sum([item_and_price[0].get_price() * item_and_price[1] for item_and_price in self.get_item_list_amount()]) + (float(settings.get_delivery_price()) if self.get_home_adress() != None else 0)
|
||||||
|
|
||||||
|
def set_item_list(self, value):
|
||||||
|
c.execute(f"UPDATE orders SET item_list=? WHERE order_id=?", [value, self.get_order_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_email_adress(self):
|
||||||
|
return self.__clist()[3]
|
||||||
|
|
||||||
|
def set_email_adress(self, value):
|
||||||
|
c.execute(f"UPDATE orders SET email_adress=? WHERE order_id=?", [value, self.get_order_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_phone_number(self):
|
||||||
|
return self.__clist()[4]
|
||||||
|
|
||||||
|
def set_phone_number(self, value):
|
||||||
|
c.execute(f"UPDATE orders SET phone_number=? WHERE order_id=?", [value, self.get_order_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_home_adress(self):
|
||||||
|
return self.__clist()[5]
|
||||||
|
|
||||||
|
def set_home_adress(self, value):
|
||||||
|
c.execute(f"UPDATE orders SET home_adress=? WHERE order_id=?", [value, self.get_order_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_additional_message(self):
|
||||||
|
return self.__clist()[6]
|
||||||
|
|
||||||
|
def get_date(self):
|
||||||
|
return datetime.strptime(self.__clist()[7], "%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
def get_date_string(self):
|
||||||
|
return self.__clist()[7]
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
return self.__clist()[8]
|
||||||
|
|
||||||
|
def get_status_string(self):
|
||||||
|
return get_status_dict()[self.__clist()[8]]
|
||||||
|
|
||||||
|
def set_status(self, value):
|
||||||
|
c.execute(f"UPDATE orders SET status=? WHERE order_id=?", [value, self.get_order_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_status_dict():
|
||||||
|
return {
|
||||||
|
0: tt.processing,
|
||||||
|
1: tt.delivery,
|
||||||
|
2: tt.done,
|
||||||
|
-1: tt.cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_order_list(status=None):
|
||||||
|
if status:
|
||||||
|
c.execute(f"SELECT * FROM orders WHERE status=?", [status])
|
||||||
|
else:
|
||||||
|
c.execute(f"SELECT * FROM orders")
|
||||||
|
return list(map(Order, [order[0] for order in list(c)]))
|
||||||
|
|
||||||
|
def does_order_exist(order_id):
|
||||||
|
c.execute(f"SELECT * FROM orders WHERE order_id=?", [order_id])
|
||||||
|
return len(list(c)) == 1
|
||||||
|
|
||||||
|
def create_order(order_id, user_id, item_list, email_adress, additional_message, phone_number="None", home_adress="None"):
|
||||||
|
c.execute(f"INSERT INTO orders VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", [order_id, user_id, item_list, email_adress, phone_number, home_adress, additional_message, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 0])
|
||||||
|
conn.commit()
|
||||||
|
return Order(order_id)
|
||||||
45
doners/shop-telegram-bot/src/search.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import pymorphy2
|
||||||
|
from pyparsing import opAssoc
|
||||||
|
import item as itm
|
||||||
|
|
||||||
|
m = pymorphy2.MorphAnalyzer()
|
||||||
|
|
||||||
|
excluded_words = ["без", "в", "для", "до", "за", "из", "к", "под", "а", "о", "над", "на", "о", "об", "от", "перед", "по", "под", "при", "про", "с", "у"]
|
||||||
|
|
||||||
|
def get_normal_forms(word):
|
||||||
|
return set(p.normal_form for p in m.parse(word))
|
||||||
|
|
||||||
|
|
||||||
|
class Query:
|
||||||
|
def __init__(self, results):
|
||||||
|
self.results = results
|
||||||
|
|
||||||
|
def __get_items(self):
|
||||||
|
return list(map(itm.Item, self.results.keys()))[::-1][:90]
|
||||||
|
|
||||||
|
def match(self):
|
||||||
|
return self.__get_items()
|
||||||
|
|
||||||
|
def price(self):
|
||||||
|
return sorted(list(map(itm.Item, self.results.keys()))[::-1], key=lambda item: item.get_price())
|
||||||
|
|
||||||
|
def popular(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# a match in a name is 3 points
|
||||||
|
# a match in a desc is 1 point
|
||||||
|
def search_item(query):
|
||||||
|
points = dict()
|
||||||
|
for item in itm.get_item_list():
|
||||||
|
points[item.get_id()] = 0
|
||||||
|
for word in list(filter(lambda word: word not in excluded_words, query.split())):
|
||||||
|
for p in get_normal_forms(word):
|
||||||
|
for word_title in item.get_name().split():
|
||||||
|
if p in get_normal_forms(word_title):
|
||||||
|
points[item.get_id()] += 3
|
||||||
|
for word_title in item.get_desc().split():
|
||||||
|
if p in get_normal_forms(word_title):
|
||||||
|
points[item.get_id()] += 1
|
||||||
|
if points[item.get_id()] == 0:
|
||||||
|
points.pop(item.get_id())
|
||||||
|
return Query(dict(sorted(points.items(), key=lambda item: item[1])))
|
||||||
170
doners/shop-telegram-bot/src/settings.py
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
from configparser import ConfigParser
|
||||||
|
from genericpath import exists
|
||||||
|
from os import remove
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self, config_path="config.ini"):
|
||||||
|
self.__config_path = config_path
|
||||||
|
|
||||||
|
def __get_config(self):
|
||||||
|
conf = ConfigParser()
|
||||||
|
conf.read(self.__config_path)
|
||||||
|
return conf
|
||||||
|
|
||||||
|
def __set_setting(self, category, subcategory, value):
|
||||||
|
conf = self.__get_config()
|
||||||
|
conf.set(category, subcategory, str(value))
|
||||||
|
with open(self.__config_path, 'w') as config:
|
||||||
|
conf.write(config)
|
||||||
|
|
||||||
|
# main_settings
|
||||||
|
def get_token(self):
|
||||||
|
return self.__get_config()["main_settings"]["token"]
|
||||||
|
|
||||||
|
def set_token(self, value):
|
||||||
|
self.__set_setting("main_settings", "token", value)
|
||||||
|
|
||||||
|
def is_debug(self):
|
||||||
|
return self.__get_config()["main_settings"]["debug"] == "1"
|
||||||
|
|
||||||
|
def set_debug(self, value):
|
||||||
|
self.__set_setting("main_settings", "debug", value)
|
||||||
|
|
||||||
|
def get_main_admin_id(self):
|
||||||
|
return self.__get_config()["main_settings"]["mainadminid"]
|
||||||
|
|
||||||
|
def set_main_admin_id(self, value):
|
||||||
|
self.__set_setting("main_settings", "mainadminid", value)
|
||||||
|
|
||||||
|
# shop_settings
|
||||||
|
def get_shop_name(self):
|
||||||
|
return self.__get_config()["shop_settings"]["name"]
|
||||||
|
|
||||||
|
def set_shop_name(self, value):
|
||||||
|
self.__set_setting("shop_settings", "name", value)
|
||||||
|
|
||||||
|
def get_shop_greeting(self):
|
||||||
|
return self.__get_config()["shop_settings"]["greeting"]
|
||||||
|
|
||||||
|
def set_shop_greeting(self, value):
|
||||||
|
self.__set_setting("shop_settings", "greeting", value)
|
||||||
|
|
||||||
|
def is_sticker_enabled(self):
|
||||||
|
return self.__get_config()["shop_settings"]["enablesticker"] == "1"
|
||||||
|
|
||||||
|
def set_enable_sticker(self, value):
|
||||||
|
self.__set_setting("shop_settings", "enablesticker", value)
|
||||||
|
|
||||||
|
def get_refund_policy(self):
|
||||||
|
return self.__get_config()["shop_settings"]["refundpolicy"]
|
||||||
|
|
||||||
|
def set_refund_policy(self, value):
|
||||||
|
self.__set_setting("shop_settings", "refundpolicy", value)
|
||||||
|
|
||||||
|
def get_shop_contacts(self):
|
||||||
|
return self.__get_config()["shop_settings"]["contacts"]
|
||||||
|
|
||||||
|
def set_shop_contacts(self, value):
|
||||||
|
self.__set_setting("shop_settings", "contacts", value)
|
||||||
|
|
||||||
|
def is_item_image_enabled(self):
|
||||||
|
return self.__get_config()["shop_settings"]["enableimage"] == "1"
|
||||||
|
|
||||||
|
def set_item_image(self, value):
|
||||||
|
self.__set_setting("shop_settings", "enableimage", value)
|
||||||
|
|
||||||
|
def is_phone_number_enabled(self):
|
||||||
|
return self.__get_config()["shop_settings"]["enablephonenumber"] == "1"
|
||||||
|
|
||||||
|
def set_enable_phone_number(self, value):
|
||||||
|
self.__set_setting("shop_settings", "enablephonenumber", value)
|
||||||
|
|
||||||
|
def is_delivery_enabled(self):
|
||||||
|
return self.__get_config()["shop_settings"]["enabledelivery"] == "1"
|
||||||
|
|
||||||
|
def set_delivery(self, value):
|
||||||
|
self.__set_setting("shop_settings", "enabledelivery", value)
|
||||||
|
|
||||||
|
def get_delivery_price(self):
|
||||||
|
return float(self.__get_config()["shop_settings"]["delivery_price"])
|
||||||
|
|
||||||
|
def set_delivery_price(self, value):
|
||||||
|
self.__set_setting("shop_settings", "delivery_price", value)
|
||||||
|
|
||||||
|
def is_captcha_enabled(self):
|
||||||
|
return self.__get_config()["shop_settings"]["enablecaptcha"] == "1"
|
||||||
|
|
||||||
|
def set_enable_captcha(self, value):
|
||||||
|
self.__set_setting("shop_settings", "enablecaptcha", value)
|
||||||
|
|
||||||
|
# stats_settings
|
||||||
|
def get_barcolor(self):
|
||||||
|
return self.__get_config()["stats_settings"]["barcolor"]
|
||||||
|
|
||||||
|
def set_barcolor(self, value):
|
||||||
|
self.__set_setting("stats_settings", "barcolor", value)
|
||||||
|
|
||||||
|
def get_borderwidth(self):
|
||||||
|
return self.__get_config()["stats_settings"]["borderwidth"]
|
||||||
|
|
||||||
|
def set_borderwidth(self, value):
|
||||||
|
self.__set_setting("stats_settings", "borderwidth", value)
|
||||||
|
|
||||||
|
def get_titlefontsize(self):
|
||||||
|
return self.__get_config()["stats_settings"]["titlefontsize"]
|
||||||
|
|
||||||
|
def set_titlefontsize(self, value):
|
||||||
|
self.__set_setting("stats_settings", "titlefontsize", value)
|
||||||
|
|
||||||
|
def get_axisfontsize(self):
|
||||||
|
return self.__get_config()["stats_settings"]["axisfontsize"]
|
||||||
|
|
||||||
|
def set_axisfontsize(self, value):
|
||||||
|
self.__set_setting("stats_settings", "axisfontsize", value)
|
||||||
|
|
||||||
|
def get_tickfontsize(self):
|
||||||
|
return self.__get_config()["stats_settings"]["tickfontsize"]
|
||||||
|
|
||||||
|
def set_tickfontsize(self, value):
|
||||||
|
self.__set_setting("stats_settings", "tickfontsize", value)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
DEFAULT_CONFIG_TEXT = f"""[main_settings]
|
||||||
|
token = {self.get_token()}
|
||||||
|
mainadminid = {self.get_main_admin_id()}
|
||||||
|
debug = 0
|
||||||
|
|
||||||
|
[shop_settings]
|
||||||
|
name = Название магазина
|
||||||
|
greeting = Добро пожаловать!
|
||||||
|
refundpolicy = Текст для вкладки "Политика возврата"
|
||||||
|
contacts = Текст для вкладки "Контакты"
|
||||||
|
enableimage = 1
|
||||||
|
enablesticker = 0
|
||||||
|
enablephonenumber = 0
|
||||||
|
enabledelivery = 0
|
||||||
|
delivery_price = 0.0
|
||||||
|
enablecaptcha = 1
|
||||||
|
|
||||||
|
[stats_settings]
|
||||||
|
barcolor = 3299ff
|
||||||
|
borderwidth = 1
|
||||||
|
titlefontsize = 20
|
||||||
|
axisfontsize = 12
|
||||||
|
tickfontsize = 8
|
||||||
|
"""
|
||||||
|
if exists("config.ini"):
|
||||||
|
remove("config.ini")
|
||||||
|
with open("config.ini", "w") as config:
|
||||||
|
config.write(DEFAULT_CONFIG_TEXT)
|
||||||
|
|
||||||
|
def clean_db(self):
|
||||||
|
conn = sqlite3.connect("data.db")
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute("DELETE FROM cats")
|
||||||
|
c.execute("DELETE FROM items")
|
||||||
|
c.execute("DELETE FROM orders")
|
||||||
|
c.execute("DELETE FROM commands")
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
111
doners/shop-telegram-bot/src/state_handler.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
from urllib import response
|
||||||
|
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||||
|
|
||||||
|
# Item management
|
||||||
|
class addCat(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
name = State()
|
||||||
|
|
||||||
|
class changeCatName(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
cat_id = State()
|
||||||
|
name = State()
|
||||||
|
|
||||||
|
class addItem(StatesGroup):
|
||||||
|
# Required
|
||||||
|
name = State()
|
||||||
|
price = State()
|
||||||
|
cat_id = State()
|
||||||
|
desc = State()
|
||||||
|
confirmation = State()
|
||||||
|
|
||||||
|
# Additional
|
||||||
|
image = State()
|
||||||
|
|
||||||
|
class changeItemPrice(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
item_id = State()
|
||||||
|
price = State()
|
||||||
|
|
||||||
|
class changeItemImage(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
item_id = State()
|
||||||
|
image = State()
|
||||||
|
|
||||||
|
class changeItemDesc(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
item_id = State()
|
||||||
|
desc = State()
|
||||||
|
|
||||||
|
class changeItemName(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
item_id = State()
|
||||||
|
name = State()
|
||||||
|
|
||||||
|
class changeItemCat(StatesGroup):
|
||||||
|
item_id = State()
|
||||||
|
cat = State()
|
||||||
|
|
||||||
|
class changeItemStock(StatesGroup):
|
||||||
|
item_id = State()
|
||||||
|
state_message = State()
|
||||||
|
stock = State()
|
||||||
|
|
||||||
|
# User management
|
||||||
|
class notifyEveryone(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
message = State()
|
||||||
|
confirmation = State()
|
||||||
|
|
||||||
|
class seeUserProfile(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
user_id = State()
|
||||||
|
|
||||||
|
|
||||||
|
# Checkout
|
||||||
|
class checkoutCart(StatesGroup):
|
||||||
|
# Data
|
||||||
|
user_id = State()
|
||||||
|
item_list_comma = State()
|
||||||
|
order_id = State()
|
||||||
|
|
||||||
|
# Required
|
||||||
|
email = State()
|
||||||
|
additional_message = State()
|
||||||
|
confirmation = State()
|
||||||
|
|
||||||
|
# Additional
|
||||||
|
phone_number = State()
|
||||||
|
home_adress = State()
|
||||||
|
captcha = State()
|
||||||
|
|
||||||
|
# Main settings
|
||||||
|
class changeShopName(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
name = State()
|
||||||
|
|
||||||
|
class changeShopGreeting(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
greeting = State()
|
||||||
|
|
||||||
|
class changeShopRefundPolicy(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
refund_policy = State()
|
||||||
|
|
||||||
|
class changeShopContacts(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
contacts = State()
|
||||||
|
|
||||||
|
class changeDeliveryPrice(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
price = State()
|
||||||
|
|
||||||
|
class addCustomCommand(StatesGroup):
|
||||||
|
command = State()
|
||||||
|
response = State()
|
||||||
|
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
class search(StatesGroup):
|
||||||
|
state_message = State()
|
||||||
|
query = State()
|
||||||
63
doners/shop-telegram-bot/src/stats.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import datetime
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import order as ordr
|
||||||
|
import user as usr
|
||||||
|
from random import randint
|
||||||
|
from settings import Settings
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
def saveplot(data, title, ylabel):
|
||||||
|
plt.autoscale()
|
||||||
|
plt.figure(figsize=(10, 10))
|
||||||
|
plt.title(title, fontsize=settings.get_titlefontsize())
|
||||||
|
plt.xlabel("Дата", fontsize=settings.get_axisfontsize())
|
||||||
|
plt.ylabel(ylabel, fontsize=settings.get_axisfontsize())
|
||||||
|
plt.tick_params(labelsize=settings.get_tickfontsize())
|
||||||
|
plt.bar(range(len(data)), list(data.values()), color="#" + settings.get_barcolor(), edgecolor="black", linewidth=settings.get_borderwidth())
|
||||||
|
plt.xticks(range(len(data)), list(data.keys()), rotation=90)
|
||||||
|
plt.savefig(f"images/stats.png")
|
||||||
|
plt.close()
|
||||||
|
return open(f"images/stats.png", "rb")
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_data():
|
||||||
|
return {f"{randint(1, 30):02}.{randint(1, 12):02}.{randint(2010, 2030)}": randint(5, 100) for _ in range(randint(2, 30))}
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_graph():
|
||||||
|
return saveplot(get_random_data(), "Название", "Ось Y")
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationCharts:
|
||||||
|
def __init__(self):
|
||||||
|
self.user_list = usr.get_user_list()
|
||||||
|
|
||||||
|
def saveplot(self, data, title):
|
||||||
|
return saveplot(data, title, "Количество регистраций")
|
||||||
|
|
||||||
|
def all_time(self):
|
||||||
|
return self.saveplot({f"{date.day:02}.{date.month:02}.{date.year}": [user.get_register_date().date() for user in self.user_list].count(date) for date in dict.fromkeys([user.get_register_date().date() for user in self.user_list])}, "Регистрации за все время")
|
||||||
|
|
||||||
|
def last_x_days(self, days):
|
||||||
|
return self.saveplot({f"{(datetime.date.today() - datetime.timedelta(days=i)).day:02}.{(datetime.date.today() - datetime.timedelta(days=i)).month:02}": len(list(filter(lambda user: user.get_register_date().date() == datetime.date.today() - datetime.timedelta(days=i), self.user_list))) for i in range(30, -1, -1)}, f"Регистрации за последние {days} дней")
|
||||||
|
|
||||||
|
def last_x_hours(self, hours):
|
||||||
|
return self.saveplot({f"{(datetime.datetime.today() - datetime.timedelta(hours=i)).hour:02}:00": len(list(filter(lambda user: user.get_register_date().hour == (datetime.datetime.now() - datetime.timedelta(hours=i)).hour and user.get_register_date() > datetime.datetime.now() - datetime.timedelta(hours=hours), self.user_list))) for i in range(hours, -1, -1)}, f"Регистрации за последние {hours} часов.")
|
||||||
|
|
||||||
|
|
||||||
|
class OrderCharts:
|
||||||
|
def __init__(self):
|
||||||
|
self.order_list = ordr.get_order_list()
|
||||||
|
|
||||||
|
def saveplot(self, data, title):
|
||||||
|
return saveplot(data, title, "Количество заказов")
|
||||||
|
|
||||||
|
def all_time(self):
|
||||||
|
return self.saveplot({f"{date.day:02}.{date.month:02}.{date.year}": [order.get_date().date() for order in self.order_list].count(date) for date in dict.fromkeys([order.get_date().date() for order in self.order_list])}, "Заказы за все время")
|
||||||
|
|
||||||
|
def last_x_days(self, days):
|
||||||
|
return self.saveplot({f"{(datetime.date.today() - datetime.timedelta(days=i)).day:02}.{(datetime.date.today() - datetime.timedelta(days=i)).month:02}": len(list(filter(lambda order: order.get_date().date() == datetime.date.today() - datetime.timedelta(days=i), self.order_list))) for i in range(days, -1, -1)}, f"Заказы за последние {days} дней")
|
||||||
|
|
||||||
|
def last_x_hours(self, hours):
|
||||||
|
return self.saveplot({f"{(datetime.datetime.today() - datetime.timedelta(hours=i)).hour:02}:00": len(list(filter(lambda order: order.get_date() > datetime.datetime.now() - datetime.timedelta(hours=hours) and order.get_date().hour == (datetime.datetime.today() - datetime.timedelta(hours=i)).hour, self.order_list))) for i in range(hours, -1, -1)}, "Заказы за сегодня" if hours == (datetime.datetime.now().hour + 1) else f"Заказы за последние {hours} часов.")
|
||||||
177
doners/shop-telegram-bot/src/text_templates.py
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
from settings import Settings
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
line_separator = "➖➖➖➖➖"
|
||||||
|
|
||||||
|
|
||||||
|
# Multiple lines
|
||||||
|
def get_profile_template(user):
|
||||||
|
return f"{line_separator}\n📝 id: {user.get_id()}\n📈 Кол-во заказов: {len(user.get_orders())}\n📅 Дата регистрации: {user.get_register_date_string()}\n{line_separator}"
|
||||||
|
|
||||||
|
def get_faq_template(shop_name):
|
||||||
|
return f"{line_separator}\nℹ️ FAQ магазина {shop_name}\n{line_separator}"
|
||||||
|
|
||||||
|
def get_categories_template():
|
||||||
|
return f"{line_separator}\n🛍️ Категории\n{line_separator}"
|
||||||
|
|
||||||
|
def get_category_was_created_successfuly(cat_name):
|
||||||
|
return f"Категория {cat_name} была успешно создана."
|
||||||
|
|
||||||
|
def get_category_data(cat):
|
||||||
|
return f"{line_separator}\nID: {cat.get_id()}\nНазвание: {cat.get_name()}\n{line_separator}"
|
||||||
|
|
||||||
|
def get_item_card(item=None, name=None, price=None, desc=None, amount=None):
|
||||||
|
if item:
|
||||||
|
name = item.get_name()
|
||||||
|
price = item.get_price()
|
||||||
|
desc = item.get_desc()
|
||||||
|
amount = item.get_amount()
|
||||||
|
|
||||||
|
return f"{line_separator}\n{name} - {'{:.2f}'.format(price)} руб.\nВ наличии: {amount} шт.\n{line_separator}\n{desc}"
|
||||||
|
|
||||||
|
def get_order_confirmation_template(item_amount_dict, cart_price, email_adress, additional_message, phone_number=None, home_adress=None):
|
||||||
|
item_amount_dict_formatted = '\n'.join([f'\t· {item[0].get_name()} - {item[1]} шт.' for item in item_amount_dict])
|
||||||
|
phone_number = f"Номер телефона: {phone_number}\n" if phone_number else ""
|
||||||
|
home_adress = f"Адрес доставки: {home_adress}\n" if home_adress else ""
|
||||||
|
return f"{line_separator}\nТовары:\n{item_amount_dict_formatted}\nСумма: {cart_price}руб.\nEmail: {email_adress}\n{phone_number}{home_adress}Комментарий к заказу: {additional_message}\n{line_separator}\nВы уверены, что хотите оформить заказ?"
|
||||||
|
|
||||||
|
def get_order_template(order):
|
||||||
|
item_list_amount_formatted = '\n'.join([f'\t· {item[0].get_name()} - {item[1]} шт.' for item in order.get_item_list_amount()])
|
||||||
|
phone_number = f"Номер телефона: {order.get_phone_number()}\n" if settings.is_phone_number_enabled() else ""
|
||||||
|
home_adress = f"Адрес доставки: {order.get_home_adress()}\n" if settings.is_delivery_enabled() else f"Самовывоз\n"
|
||||||
|
return f"{line_separator}\nID заказа: {order.get_order_id()}\nID пользователя: {order.get_user_id()}\nТовары:\n{item_list_amount_formatted}\nСумма: {order.get_item_list_price()}руб.\nEmail: {order.get_email_adress()}\n{phone_number}{home_adress}Комментарий к заказу: {order.get_additional_message()}\nСтатус заказа: {order.get_status_string()}\nДата: {order.get_date_string()}\n{line_separator}"
|
||||||
|
|
||||||
|
# Single phrases
|
||||||
|
# /start
|
||||||
|
admin_panel = "🔴 Админ панель"
|
||||||
|
faq = "ℹ️ FAQ"
|
||||||
|
profile = "📁 Профиль"
|
||||||
|
catalogue = "🗄️ Каталог"
|
||||||
|
cart = "🛒 Корзина"
|
||||||
|
support_menu = "☎ Меню тех. поддержки"
|
||||||
|
|
||||||
|
# Admin panel tabs
|
||||||
|
item_management = "📦 Управление товаром"
|
||||||
|
user_management = "🧍 Управление пользователями"
|
||||||
|
shop_stats = "📈 Статистика магазина (BETA)"
|
||||||
|
bot_settings = "⚙ Настройки бота"
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
contacts = "📞 Контакты"
|
||||||
|
refund = "🎫 Политика возврата"
|
||||||
|
|
||||||
|
# Profile
|
||||||
|
my_orders = "📂 Мои заказы"
|
||||||
|
cancel_order = "❌ Отменить заказ"
|
||||||
|
restore_order = "✅ Восстановить заказ"
|
||||||
|
my_support_tickets = "🙋 Мои тикеты в тех. поддержку"
|
||||||
|
enable_notif = "🔔Включить оповещения о заказах"
|
||||||
|
disable_notif = "🔕Выключить оповещения о заказах"
|
||||||
|
|
||||||
|
# Catalogue / Item / Cart
|
||||||
|
search = "🔍 Найти"
|
||||||
|
add_to_cart = "🛒 Добавить в корзину"
|
||||||
|
cart_is_empty = "Корзина пуста."
|
||||||
|
pickup = "✅Самовывоз"
|
||||||
|
def delivery_on(price): return f"✅ Доставка - {price}руб."
|
||||||
|
def delivery_off(price): return f"❌ Доставка - {price}руб."
|
||||||
|
cart_checkout = "Оформить заказ"
|
||||||
|
clear_cart = "Очистить корзину"
|
||||||
|
processing = "Обрабатывается"
|
||||||
|
delivery = "Ожидает доставки"
|
||||||
|
done = "Готов"
|
||||||
|
cancelled = "Отменён"
|
||||||
|
|
||||||
|
# Item management
|
||||||
|
add_cat = "🛍️ Добавить категорию"
|
||||||
|
add_item = "🗃️ Добавить товар"
|
||||||
|
edit_cat = "✏️ Редактировать категорию"
|
||||||
|
edit_item = "✏️ Редактировать товар"
|
||||||
|
change_name = "📋 Изменить название"
|
||||||
|
change_image = "🖼️ Изменить изображение"
|
||||||
|
hide_image = "🙈 Скрыть изображение"
|
||||||
|
show_image = "🐵 Показать изображение"
|
||||||
|
change_desc = "📝 Изменить описание"
|
||||||
|
change_price = "🏷️ Изменить цену"
|
||||||
|
change_item_cat = "🛍️ Изменить категорию"
|
||||||
|
change_stock = "📦 Изменить кол-во"
|
||||||
|
|
||||||
|
# User management
|
||||||
|
user_profile = "📁Профиль пользователя"
|
||||||
|
notify_everyone = "🔔Оповещение всем пользователям"
|
||||||
|
orders = "📁 Заказы"
|
||||||
|
remove_manager_role = "👨💼 Убрать роль менеджера"
|
||||||
|
add_manager_role = "👨💼 Сделать менеджером"
|
||||||
|
remove_admin_role = "🔴 Убрать роль администратора"
|
||||||
|
add_admin_role = "🔴 Сделать администратором"
|
||||||
|
def change_order_status(status): return f"Изменить статус на \"{status}\""
|
||||||
|
|
||||||
|
# Shop stats
|
||||||
|
registration_stats = "👥Статистика регистраций"
|
||||||
|
order_stats = "📦Статистика заказов"
|
||||||
|
all_time = "За всё время"
|
||||||
|
monthly = "За последние 30 дней"
|
||||||
|
weekly = "За последние 7 дней"
|
||||||
|
daily = "За последние 24 часа"
|
||||||
|
|
||||||
|
# Shop settings
|
||||||
|
main_settings = "🛠️ Основные настройки"
|
||||||
|
item_settings = "🗃️ Настройки товаров"
|
||||||
|
additional_settings = "📖 Дополнительные настройки"
|
||||||
|
custom_commands = "📖 Команды"
|
||||||
|
add_command = "📝 Добавить команду"
|
||||||
|
clean_logs = "📖 Очистить логи"
|
||||||
|
clean_logs_text = "⚠️ Вы уверены, что хотите очистить логи? Они будут удалены без возможности восстановления!\n(Логи за сегодняшний день не будут удалены)"
|
||||||
|
backups = "💾 Резервное копирование"
|
||||||
|
update_backup = "🔄 Обновить резервную копию"
|
||||||
|
load_backup = "💿 Загрузить резервную копию"
|
||||||
|
clean_backups = "🧹 Очистка резервных копий"
|
||||||
|
system_settings = "💻 Система"
|
||||||
|
clean_images = "🗑️ Удалить неиспользуемые изображения"
|
||||||
|
clean_images_text = "⚠️ Вы уверены, что хотите удалить неспользуемые изображения? Они будут удалены без возможности восстановления!"
|
||||||
|
clean_database = "📚 Очистить базу данных"
|
||||||
|
clean_database_text = "⚠️ Вы уверены, что хотите очистить базу данных? Все данные будут удалены без возможности восстановления!"
|
||||||
|
reset_settings = "⚙️ Сбросить настройки"
|
||||||
|
resert_settings_text = "⚠️ Вы уверены, что хотите сбросить настройки? Все данные будут удалены без возможности восстановления!"
|
||||||
|
disable_item_image = "✅ Картинки товаров"
|
||||||
|
enable_item_image = "❌ Картинки товаров"
|
||||||
|
checkout_settings = "💳 Настройки оформления заказа"
|
||||||
|
stats_settings = "📈 Настройки статистики"
|
||||||
|
graph_color = "🌈 Цвет графика"
|
||||||
|
border_width = "🔲 Ширина обводки"
|
||||||
|
title_font_size = "ℹ️ Размер названия графика"
|
||||||
|
axis_font_size = "↔️Размер текста для осей"
|
||||||
|
tick_font_size = "🔢Размер текста для делений"
|
||||||
|
unavailable = "⛔️"
|
||||||
|
minus = "➖"
|
||||||
|
plus = "➕"
|
||||||
|
enable_sticker = "❌ Стикер в приветствии"
|
||||||
|
disable_sticker = "✅ Стикер в приветствии"
|
||||||
|
enable_phone_number = "❌ Номер телефона при заказе"
|
||||||
|
disable_phone_number = "✅ Номер телефона при заказе"
|
||||||
|
enable_delivery = "❌ Доставка"
|
||||||
|
disable_delivery = "✅ Доставка"
|
||||||
|
def delivery_price(price): return f"🚚 Стоимость доставки: {price}руб."
|
||||||
|
enable_captcha = "❌ CAPTCHA при заказе"
|
||||||
|
disable_captcha = "✅ CAPTCHA при заказе"
|
||||||
|
enable_debug = "❌ Режим отладки"
|
||||||
|
disable_debug = "✅ Режим отладки"
|
||||||
|
|
||||||
|
# Manager tab
|
||||||
|
view_order = "📂 Посмотреть заказ"
|
||||||
|
|
||||||
|
# Misc buttons
|
||||||
|
skip = "⏭ Пропустить"
|
||||||
|
back = "🔙 Назад"
|
||||||
|
confirm = "✅ Да"
|
||||||
|
deny = "❌ Нет"
|
||||||
|
error = "Произошла ошибка!"
|
||||||
|
or_press_back = "или нажмите на кнопку \"Назад\"."
|
||||||
|
hide = "🙈 Скрыть"
|
||||||
|
show = "🐵 Показать"
|
||||||
|
delete = "❌ Удалить"
|
||||||
|
reset = "❌ Сбросить"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(delivery_on)
|
||||||
111
doners/shop-telegram-bot/src/user.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import sqlite3
|
||||||
|
from datetime import datetime
|
||||||
|
import item as itm
|
||||||
|
from order import Order
|
||||||
|
from settings import Settings
|
||||||
|
|
||||||
|
conn = sqlite3.connect('data.db')
|
||||||
|
c = conn.cursor()
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
class User:
|
||||||
|
def __init__(self, user_id):
|
||||||
|
self.__user_id = user_id
|
||||||
|
|
||||||
|
if not does_user_exist(self.get_id()):
|
||||||
|
c.execute(f"INSERT INTO users VALUES(?, ?, ?, ?, ?, ?, ?)", [self.get_id(), 1 if str(self.get_id()) == settings.get_main_admin_id() else 0, 0, 0, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "None", 1])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.__user_id
|
||||||
|
|
||||||
|
def __clist(self):
|
||||||
|
c.execute(f"SELECT * FROM users WHERE user_id=?", [self.get_id()])
|
||||||
|
return list(c)[0]
|
||||||
|
|
||||||
|
def is_admin(self):
|
||||||
|
return self.__clist()[1] == 1
|
||||||
|
|
||||||
|
def set_admin(self, value):
|
||||||
|
c.execute(f"UPDATE users SET is_admin=? WHERE user_id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def is_manager(self):
|
||||||
|
return self.__clist()[2] == 1
|
||||||
|
|
||||||
|
def set_manager(self, value):
|
||||||
|
c.execute(f"UPDATE users SET is_manager=? WHERE user_id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_register_date(self):
|
||||||
|
return datetime.strptime(self.__clist()[4], "%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
def get_register_date_string(self):
|
||||||
|
return self.__clist()[4]
|
||||||
|
|
||||||
|
def notif_on(self):
|
||||||
|
return self.__clist()[3] == 1
|
||||||
|
|
||||||
|
def set_notif_enable(self, value):
|
||||||
|
c.execute(f"UPDATE users SET notification=? WHERE user_id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_orders(self):
|
||||||
|
c.execute(f"SELECT * FROM orders WHERE user_id=?", [self.get_id()])
|
||||||
|
return list(map(Order, [order[0] for order in list(c)]))[::-1]
|
||||||
|
|
||||||
|
def get_cart_comma(self):
|
||||||
|
return self.__clist()[5]
|
||||||
|
|
||||||
|
def get_cart(self):
|
||||||
|
cart = self.get_cart_comma()
|
||||||
|
return [] if cart == "None" else list(map(itm.Item, cart.split(",")))
|
||||||
|
|
||||||
|
def get_cart_amount(self):
|
||||||
|
cart = [item.get_id() for item in self.get_cart()]
|
||||||
|
return [[itm.Item(item_id), cart.count(item_id)] for item_id in set(cart)]
|
||||||
|
|
||||||
|
def get_cart_price(self):
|
||||||
|
return sum([item_and_price[0].get_price() * item_and_price[1] for item_and_price in self.get_cart_amount()]) + (settings.get_delivery_price() if self.is_cart_delivery() else 0)
|
||||||
|
|
||||||
|
def clear_cart(self):
|
||||||
|
c.execute(f"UPDATE users SET cart=\"None\" WHERE user_id=?", [self.get_id()])
|
||||||
|
self.set_cart_delivery(1)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def add_to_cart(self, item_id):
|
||||||
|
cart = self.get_cart()
|
||||||
|
c.execute(f"UPDATE users SET cart=? WHERE user_id=?", [",".join([str(item.get_id()) for item in cart + [itm.Item(item_id)]]) if cart else item_id, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def remove_from_cart(self, item_id):
|
||||||
|
cart = [item.get_id() for item in self.get_cart()]
|
||||||
|
cart.remove(str(item_id))
|
||||||
|
c.execute(f"UPDATE users SET cart=? WHERE user_id=?", [",".join(cart) if cart else "None", self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def is_cart_delivery(self):
|
||||||
|
return self.__clist()[6] == 1
|
||||||
|
|
||||||
|
def set_cart_delivery(self, value):
|
||||||
|
c.execute(f"UPDATE users SET cart_delivery=? WHERE user_id=?", [value, self.get_id()])
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def does_user_exist(user_id):
|
||||||
|
c.execute(f"SELECT * FROM users WHERE user_id=?", [user_id])
|
||||||
|
return len(list(c)) != 0
|
||||||
|
|
||||||
|
|
||||||
|
def get_notif_list():
|
||||||
|
c.execute(f"SELECT * FROM users WHERE notification=1")
|
||||||
|
return list(map(User, [user[0] for user in list(c)]))
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_login(message):
|
||||||
|
return message.from_user.username
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_list():
|
||||||
|
c.execute("SELECT * FROM users")
|
||||||
|
return list(map(User, [user[0] for user in list(c)]))
|
||||||
3
doners/shop-telegram-bot/start.cmd
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cls
|
||||||
|
echo "Starting the bot..."
|
||||||
|
python3 src/main.py
|
||||||
4
doners/shop-telegram-bot/start.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
clear
|
||||||
|
echo "Starting the bot..."
|
||||||
|
python3.10 src/main.py
|
||||||
|
|
||||||
0
doners/shop-telegram-bot/tests/__init__.py
Normal file
127
doners/shop-telegram-bot/tests/test_item.py
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import unittest
|
||||||
|
from random import randint
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
import src.item as itm
|
||||||
|
import src.category as cat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestItem(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.test_data_random = {
|
||||||
|
"name": get_random_string(10, 40),
|
||||||
|
"price": get_random_float(100, 10000),
|
||||||
|
"cat_id": randint(1, 100),
|
||||||
|
"desc": get_random_string(10, 255),
|
||||||
|
}
|
||||||
|
self.item_random = itm.create_item(self.test_data_random["name"], self.test_data_random["price"], self.test_data_random["cat_id"], self.test_data_random["desc"], active=False)
|
||||||
|
|
||||||
|
self.test_data = {
|
||||||
|
"name": "Test Item Name",
|
||||||
|
"price": 999.99,
|
||||||
|
"cat_id": cat.get_cat_list()[0].get_id(),
|
||||||
|
"desc": "Test Item's description here. Blah-blah-blah..."
|
||||||
|
}
|
||||||
|
self.item = itm.create_item(self.test_data["name"], self.test_data["price"], self.test_data["cat_id"], self.test_data["desc"], active=False)
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.item.delete()
|
||||||
|
|
||||||
|
def test_name(self):
|
||||||
|
self.assertEqual(self.item_random.get_name(), self.test_data_random["name"])
|
||||||
|
name_random = get_random_string(2, 20)
|
||||||
|
self.item_random.set_name(name_random)
|
||||||
|
self.test_data_random["name"] = name_random
|
||||||
|
self.assertEqual(self.item_random.get_name(), self.test_data_random["name"])
|
||||||
|
|
||||||
|
self.assertEqual(self.item.get_name(), self.test_data["name"])
|
||||||
|
name = "New test Item name"
|
||||||
|
self.item.set_name(name)
|
||||||
|
self.test_data["name"] = name
|
||||||
|
self.assertEqual(self.item.get_name(), self.test_data["name"])
|
||||||
|
|
||||||
|
def test_price(self):
|
||||||
|
self.assertEqual(self.item_random.get_price(), self.test_data_random["price"])
|
||||||
|
price_random = get_random_float(10, 1000)
|
||||||
|
self.item_random.set_price(price_random)
|
||||||
|
self.test_data_random["price"] = price_random
|
||||||
|
self.assertEqual(self.item_random.get_price(), self.test_data_random["price"])
|
||||||
|
|
||||||
|
self.assertEqual(self.item.get_price(), self.test_data["price"])
|
||||||
|
price = 123.12
|
||||||
|
self.item.set_price(price)
|
||||||
|
self.test_data["price"] = price
|
||||||
|
self.assertEqual(self.item.get_price(), self.test_data["price"])
|
||||||
|
|
||||||
|
def test_cat_id(self):
|
||||||
|
self.assertEqual(self.item_random.get_cat_id(), self.test_data_random["cat_id"])
|
||||||
|
cat_id_random = randint(1, 100)
|
||||||
|
self.item_random.set_cat_id(cat_id_random)
|
||||||
|
self.test_data_random["cat_id"] = cat_id_random
|
||||||
|
self.assertEqual(self.item_random.get_cat_id(), self.test_data_random["cat_id"])
|
||||||
|
|
||||||
|
self.assertEqual(self.item.get_cat_id(), self.test_data["cat_id"])
|
||||||
|
cat_id = 2
|
||||||
|
self.item.set_cat_id(cat_id)
|
||||||
|
self.test_data["cat_id"] = cat_id
|
||||||
|
self.assertEqual(self.item.get_cat_id(), self.test_data["cat_id"])
|
||||||
|
|
||||||
|
def test_desc(self):
|
||||||
|
self.assertEqual(self.item_random.get_desc(), self.test_data_random["desc"])
|
||||||
|
desc_random = get_random_string(10, 255)
|
||||||
|
self.item_random.set_desc(desc_random)
|
||||||
|
self.test_data_random["desc"] = desc_random
|
||||||
|
self.assertEqual(self.item_random.get_desc(), self.test_data_random["desc"])
|
||||||
|
|
||||||
|
self.assertEqual(self.item.get_desc(), self.test_data["desc"])
|
||||||
|
desc = "New desc"
|
||||||
|
self.item.set_desc(desc)
|
||||||
|
self.test_data["desc"] = desc
|
||||||
|
self.assertEqual(self.item.get_desc(), self.test_data["desc"])
|
||||||
|
|
||||||
|
def test_active(self):
|
||||||
|
self.assertFalse(self.item.is_active())
|
||||||
|
|
||||||
|
self.item.set_active(1)
|
||||||
|
self.assertTrue(self.item.is_active())
|
||||||
|
self.item.set_active(0)
|
||||||
|
self.assertFalse(self.item.is_active())
|
||||||
|
|
||||||
|
def test_amount(self):
|
||||||
|
self.assertEqual(self.item_random.get_amount(), 0)
|
||||||
|
amount_random = randint(1, 1000)
|
||||||
|
self.item_random.set_amount(amount_random)
|
||||||
|
self.test_data_random["amount"] = amount_random
|
||||||
|
self.assertEqual(self.item_random.get_amount(), self.test_data_random["amount"])
|
||||||
|
|
||||||
|
self.assertEqual(self.item.get_amount(), 0)
|
||||||
|
amount = 90
|
||||||
|
self.item.set_amount(amount)
|
||||||
|
self.test_data["amount"] = amount
|
||||||
|
self.assertEqual(self.item.get_amount(), self.test_data["amount"])
|
||||||
|
|
||||||
|
# TODO: image test cases
|
||||||
|
@unittest.SkipTest
|
||||||
|
def test_get_image_id(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@unittest.SkipTest
|
||||||
|
def test_get_image(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@unittest.SkipTest
|
||||||
|
def test_set_image_id(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_hide_image(self):
|
||||||
|
self.assertFalse(self.item.is_hide_image())
|
||||||
|
self.item.set_hide_image(1)
|
||||||
|
self.assertTrue(self.item.is_hide_image())
|
||||||
|
self.item.set_hide_image(0)
|
||||||
|
self.assertFalse(self.item.is_hide_image())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
8
doners/shop-telegram-bot/tests/utils.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from random import randint, choice
|
||||||
|
from string import ascii_lowercase, digits
|
||||||
|
|
||||||
|
def get_random_string(a, b):
|
||||||
|
return ''.join([choice(ascii_lowercase+digits) for _ in range(randint(a, b))])
|
||||||
|
|
||||||
|
def get_random_float(a, b):
|
||||||
|
return float(f"{randint(a, b)}.{randint(0, 99)}")
|
||||||
215
doners/shopbot/README.md
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# Сообщение от разработчика (31.01.2023)
|
||||||
|
Всем привет, код писался мною когда я только учился пайтону, просьба не используйте данный код в коммерческих целях.
|
||||||
|
|
||||||
|
Скажу откровенно, код очень не граммотный и требует полного рефакторинга
|
||||||
|
|
||||||
|
Почему? Потому что данный код написан в одном файле что является очень плохим способом.
|
||||||
|
|
||||||
|
Кому не лень - перепишите, я посмотрю и закомичу Ваш апдейт :D
|
||||||
|
|
||||||
|
Возможно я ещё возьмусь за данного бота и переработаю его чтобы Вы могли использовать в коммерческих целях и в личных :D
|
||||||
|
|
||||||
|
Спасибо!
|
||||||
|
|
||||||
|
Так-же хочу выразить глубокую благодарность за ваши звёзды и форки, приятно это видеть, даже если код откровенно ужасен.
|
||||||
|
|
||||||
|
Хочу посоветовать Вам, братья учите пайтон, не пожалеете. Данный код пока что используйте только в ознакомительных целях, не думайте что он хороший и его нужно использовать.
|
||||||
|
|
||||||
|
# Обновление 4.0 (18.10.2021)
|
||||||
|
✅ Обновления:
|
||||||
|
1. Был полностью оптимизирован код, теперь если пользователь введёт что-то не так, бот не будет крашиться, а напишет
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Инлайновые кнопки были полностью изменены, вместо текста, эмодзи.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. Обновлён профиль пользователя
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. Добавлена команда для просмотра всех пользователей `/users` (Синяя ссылка это кликабельно)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Обновление 3.0 (17.10.2021)
|
||||||
|
✅ Обновления:
|
||||||
|
1. Добавлена команда для просмотра купленных товаров `/mybuy`
|
||||||
|
2. При покупке товара, если у пользователя уже куплен товар пишет
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. Добавлена команда для связи с тех.поддержкой `/teh`
|
||||||
|
4. Для администратора добавлена команда `/ot` для ответа пользователю (отправки сообщения пользователю)
|
||||||
|
|
||||||
|
# Обновление 2.0 (17.10.2021)
|
||||||
|
|
||||||
|
✅ Обновления:
|
||||||
|
1. Добавлена команда для удаления товаров `/rembuy`
|
||||||
|
2. Добавлена команда для изменения данных о товаре (Название, Цена, Ссылка) `/editbuy`
|
||||||
|
3. Код стал более оптимизированым
|
||||||
|
|
||||||
|
# ShopBot Telegram
|
||||||
|
|
||||||
|
🤖 Бот Магазин для Телеграмма на Python 🤖
|
||||||
|
|
||||||
|
|
||||||
|
✅ Функционал бота:
|
||||||
|
1. Уровни доступа (Пользователь, Администратор, Разработчик)
|
||||||
|
2. База данных SQLite3
|
||||||
|
3. Добавление товаров
|
||||||
|
4. Продажа товаров
|
||||||
|
5. Пополнение счёта при помощи QIWI (Библиотека SimpleQIWI)
|
||||||
|
6. Просмотр профилей других пользователей
|
||||||
|
7. Выдача средств, выдача уровней доступа.
|
||||||
|
8. Изменение данных о товаре
|
||||||
|
9. Удаление товаров
|
||||||
|
10. Список купленных товаров
|
||||||
|
11. Система тех.поддержки
|
||||||
|
|
||||||
|
🎄 В будущем:
|
||||||
|
1. Ссылка для оплаты при попоплнении счёта
|
||||||
|
|
||||||
|
Список будет дополняться и изменятся.
|
||||||
|
|
||||||
|
📄 Список библиотек которые нужно установить:
|
||||||
|
|
||||||
|
`pip install pyTelegramBotAPI`
|
||||||
|
`pip install SimpleQIWI`
|
||||||
|
`pip install requests`
|
||||||
|
|
||||||
|
👾 Список команд:
|
||||||
|
|
||||||
|
`/start` - Зарегистрироваться в боте
|
||||||
|
|
||||||
|
`/profile` - Профиль
|
||||||
|
|
||||||
|
`/donate` - Пополнить счёт
|
||||||
|
|
||||||
|
`/buy` - Купить товар
|
||||||
|
|
||||||
|
`/help` - Помощь по командам
|
||||||
|
|
||||||
|
`/mybuy` - Список купленных товаров
|
||||||
|
|
||||||
|
`/teh` - Связаться с тех.поддержкой
|
||||||
|
|
||||||
|
🚨 Админские команды:
|
||||||
|
|
||||||
|
`/getprofile` - Посмотреть чужой профиль
|
||||||
|
|
||||||
|
`/getid` - Узнать ID пользователя
|
||||||
|
|
||||||
|
`/getcid` - Узнать Conference ID
|
||||||
|
|
||||||
|
`/access` - Выдать уровень доступа
|
||||||
|
|
||||||
|
`/giverub` - Выдать средства пользователю
|
||||||
|
|
||||||
|
`/addbuy` - Добавить товар
|
||||||
|
|
||||||
|
`/getrazrab` - Выдать себе разработчика (читать установку)
|
||||||
|
|
||||||
|
`/editbuy` - Изменить данные о товаре
|
||||||
|
|
||||||
|
`/rembuy` - Удалить товар
|
||||||
|
|
||||||
|
`/ot` - Ответить пользователю (отправить сообщение)
|
||||||
|
|
||||||
|
|
||||||
|
# 🛠 Установка бота:
|
||||||
|
1. Скачать файлы, затем распоковать папку в любое удобное место.
|
||||||
|
2. Открыть папку затем открыть файл `configure.py`
|
||||||
|
3. Найти бота `@BotFather` в телеграмм затем написать `/newbot`
|
||||||
|
4. Ввести имя бота, ник бота затем вылезет токен
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5. Копируем токен, затем открываем файл `configure.py`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
У вас будет пустой файл в строку `'name'` нужно вписать имя бота пример:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
В строку `'token'` нужно вписать токен который мы получили ранее:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Далее переходим на сайт `qiwi.com/api`
|
||||||
|
Затем нажимаем на кнопку Выпустить новый токен
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Выбираем все галочки, нажимаем на кнопку выбрать всё, затем нажимаем продолжить
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Далее копируем токен и вставляем его в строку `'tokenqiwi'`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Далее в строку `'phoneqiwi'` вставляем свой номер телефона от QIWI кошелька
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Обязательно с + и кодом страны
|
||||||
|
|
||||||
|
Отлично! Всё почти готово.
|
||||||
|
|
||||||
|
Переходим по ссылке которая в сообщении с токеном бота
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Запускаем бота открывая файл `start.bat`
|
||||||
|
|
||||||
|
Далее нажимаем кнопку `Начать` либо `/start`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Изменить этот текст Вы сможете тут
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Весь исходный код находиться в файле `bot.py`
|
||||||
|
|
||||||
|
Открываем файл `bot.py` затем ищем строку
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
В чате с ботом пишем `/profile`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Копируем ваш ID затем вставялем в строку `WHERE id = 596060542` и `if message.from_user.id == 596060542:` ваш ID
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Затем сохраняем и перезапускаем бота.
|
||||||
|
В чат с ботом пишем `/getrazrab` и всё! Вы получили админку разработчика
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Далее находим строку 223 'def donateyesoplacheno'
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
И вместо `596060542` вписываем свой ID
|
||||||
|
|
||||||
|
Всё, бот полностью настроен!
|
||||||
|
Советую поставить бота на хостинг по типу:
|
||||||
|
|
||||||
|
`eu.pythonanywhere.com`
|
||||||
|
|
||||||
|
Либо на ВДС или на Выделенный сервер
|
||||||
|
|
||||||
|
Бот делал лично Я с нуля, один. Просьба если будете его пересливать, дорабатывать и сливать то указывайте автора исходного кода. ❤
|
||||||
|
|
||||||
|
# 📞 Связь со мной:
|
||||||
|
|
||||||
|
1. ВК: https://vk.com/nosemka
|
||||||
|
2. Дискорд: semmy#0068
|
||||||
883
doners/shopbot/bot.py
Normal file
@@ -0,0 +1,883 @@
|
|||||||
|
import telebot
|
||||||
|
import configure
|
||||||
|
import sqlite3
|
||||||
|
from telebot import types
|
||||||
|
import threading
|
||||||
|
from requests import get
|
||||||
|
from time import sleep
|
||||||
|
from SimpleQIWI import *
|
||||||
|
|
||||||
|
client = telebot.TeleBot(configure.config['token'])
|
||||||
|
db = sqlite3.connect('baza.db', check_same_thread=False)
|
||||||
|
sql = db.cursor()
|
||||||
|
lock = threading.Lock()
|
||||||
|
api = QApi(token=configure.config['tokenqiwi'], phone=configure.config['phoneqiwi'])
|
||||||
|
markdown = """
|
||||||
|
*bold text*
|
||||||
|
_italic text_
|
||||||
|
[text](URL)
|
||||||
|
"""
|
||||||
|
|
||||||
|
#database
|
||||||
|
|
||||||
|
sql.execute("""CREATE TABLE IF NOT EXISTS users (id BIGINT, nick TEXT, cash INT, access INT, bought INT)""")
|
||||||
|
sql.execute("""CREATE TABLE IF NOT EXISTS shop (id INT, name TEXT, price INT, tovar TEXT, whobuy TEXT)""")
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
@client.message_handler(commands=['start'])
|
||||||
|
def start(message):
|
||||||
|
try:
|
||||||
|
getname = message.from_user.first_name
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
|
||||||
|
sql.execute(f"SELECT id FROM users WHERE id = {uid}")
|
||||||
|
if sql.fetchone() is None:
|
||||||
|
sql.execute(f"INSERT INTO users VALUES ({uid}, '{getname}', 0, 0, 0)")
|
||||||
|
client.send_message(cid, f"🛒 | Добро пожаловать, {getname}!\nТы попал в бота магазин\nИзмените этот текст!")
|
||||||
|
db.commit()
|
||||||
|
else:
|
||||||
|
client.send_message(cid, f"⛔️ | Ты уже зарегистрирован! Пропиши /help чтобы узнать команды.")
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['profile', 'myinfo', 'myprofile'])
|
||||||
|
def myprofile(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
if getaccess == 0:
|
||||||
|
accessname = 'Пользователь'
|
||||||
|
elif getaccess == 1:
|
||||||
|
accessname = 'Администратор'
|
||||||
|
elif getaccess == 777:
|
||||||
|
accessname = 'Разработчик'
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
client.send_message(cid, f"*📇 | Твой профиль:*\n\n*👤 | Ваш ID:* {info[0]}\n*💸 | Баланс:* {info[2]} ₽\n*👑 | Уровень доступа:* {accessname}\n*🛒 | Куплено товаров:* {info[4]}\n\n*🗂 Чтобы посмотреть список купленных товаров напишите /mybuy*", parse_mode='Markdown')
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['users'])
|
||||||
|
def allusers(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
accessquery = 1
|
||||||
|
if getaccess < accessquery:
|
||||||
|
client.send_message(cid, '⚠️ | У вас нет доступа!')
|
||||||
|
else:
|
||||||
|
text = '*🗃 | Список всех пользователей:*\n\n'
|
||||||
|
idusernumber = 0
|
||||||
|
for info in sql.execute(f"SELECT * FROM users"):
|
||||||
|
if info[3] == 0:
|
||||||
|
accessname = 'Пользователь'
|
||||||
|
elif info[3] == 1:
|
||||||
|
accessname = 'Администратор'
|
||||||
|
elif info[3] == 777:
|
||||||
|
accessname = 'Разработчик'
|
||||||
|
idusernumber += 1
|
||||||
|
text += f"*{idusernumber}. {info[0]} ({info[1]})*\n*💸 | Баланс:* {info[2]} ₽\n*👑 | Уровень доступа:* {accessname}\n*✉️ | Профиль:*" + f" [{info[1]}](tg://user?id="+str(info[0])+")\n\n"
|
||||||
|
client.send_message(cid, f"{text}",parse_mode='Markdown')
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['mybuy'])
|
||||||
|
def mybuy(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
text = '*🗂 | Список купленных товаров:*\n\n'
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop"):
|
||||||
|
if str(info[0]) in infoshop[4]:
|
||||||
|
text += f"*{infoshop[0]}. {infoshop[1]}*\nТовар: {infoshop[3]}\n\n"
|
||||||
|
client.send_message(cid,f"{text}",parse_mode='Markdown',disable_web_page_preview=True)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['getprofile', 'info'])
|
||||||
|
def getprofile(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
accessquery = 1
|
||||||
|
if getaccess < accessquery:
|
||||||
|
client.send_message(cid, '⚠️ | У вас нет доступа!')
|
||||||
|
else:
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
msg = client.send_message(cid, f'Введите ID пользователя:\nПример: {info[0]}')
|
||||||
|
client.register_next_step_handler(msg, getprofile_next)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def getprofile_next(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
getprofileid = message.text
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {getprofileid}"):
|
||||||
|
if info[3] == 0:
|
||||||
|
accessname = 'Пользователь'
|
||||||
|
elif info[3] == 1:
|
||||||
|
accessname = 'Администратор'
|
||||||
|
elif info[3] == 777:
|
||||||
|
accessname = 'Разработчик'
|
||||||
|
client.send_message(cid, f"*📇 | Профиль {info[1]}:*\n\n*ID пользователя:* {info[0]}\n*Баланс:* {info[2]} ₽\n*Уровень доступа:* {accessname}\n*Куплено товаров:* {info[4]}",parse_mode='Markdown')
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['editbuy'])
|
||||||
|
def editbuy(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
accessquery = 1
|
||||||
|
with lock:
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
if getaccess < 1:
|
||||||
|
client.send_message(cid, '⚠️ | У вас нет доступа!')
|
||||||
|
else:
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_name = types.InlineKeyboardButton(text='Название',callback_data='editbuyname')
|
||||||
|
item_price = types.InlineKeyboardButton(text='Цена',callback_data='editbuyprice')
|
||||||
|
item_tovar = types.InlineKeyboardButton(text='Товар',callback_data='editbuytovar')
|
||||||
|
rmk.add(item_name, item_price, item_tovar)
|
||||||
|
msg = client.send_message(cid, f"🔰 | Выберите что Вы хотите изменить:",reply_markup=rmk,parse_mode='Markdown')
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def editbuy_name(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global editbuynameidtovar
|
||||||
|
editbuynameidtovar = int(message.text)
|
||||||
|
msg = client.send_message(cid, f"*Введите новое название товара:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, editbuy_name_new_name)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def editbuy_name_new_name(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global editbuynametovar
|
||||||
|
editbuynametovar = message.text
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop WHERE id = {editbuynameidtovar}"):
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅', callback_data='editbuynewnametovaryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌', callback_data='editbuynewnametovarno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"*🔰 | Данные об изменении названия товара:*\n\nID товара: {editbuynameidtovar}\nСтарое имя товара: {infoshop[1]}\nНовое имя товара: {editbuynametovar}\n\nВы подверждаете изменения?",parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def editbuy_price(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global editbuypriceidtovar
|
||||||
|
editbuypriceidtovar = int(message.text)
|
||||||
|
msg = client.send_message(cid, f"*Введите новую цену товара:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, editbuy_price_new_price)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def editbuy_price_new_price(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global editbuypricetovar
|
||||||
|
editbuypricetovar = int(message.text)
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop WHERE id = {editbuypriceidtovar}"):
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅', callback_data='editbuynewpricetovaryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌', callback_data='editbuynewpricetovarno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"*🔰 | Данные об изменении цены товара:*\n\nID товара: {editbuypriceidtovar}\nСтарая цена: {infoshop[2]}\nНовая цена: {editbuypricetovar}\n\nВы подверждаете изменения?",parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def editbuy_tovar(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global editbuytovaridtovar
|
||||||
|
editbuytovaridtovar = int(message.text)
|
||||||
|
msg = client.send_message(cid, f"*Введите новую ссылку на товар:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, editbuy_tovar_new_tovar)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def editbuy_tovar_new_tovar(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global editbuytovartovar
|
||||||
|
editbuytovartovar = message.text
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop WHERE id = {editbuytovaridtovar}"):
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅', callback_data='editbuynewtovartovaryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌', callback_data='editbuynewtovartovarno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"*🔰 | Данные об изменении сcылки товара:*\n\nID товара: {editbuytovaridtovar}\nСтарая ссылка: {infoshop[3]}\nНовая ссылка: {editbuytovartovar}\n\nВы подверждаете изменения?",parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'editbuynewtovartovaryes' or call.data == 'editbuynewtovartovarno')
|
||||||
|
def editbuy_tovar_new_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'editbuynewtovartovaryes':
|
||||||
|
sql.execute(f"SELECT * FROM shop WHERE id = {editbuytovaridtovar}")
|
||||||
|
sql.execute(f"UPDATE shop SET tovar = '{editbuytovartovar}' WHERE id = {editbuytovaridtovar}")
|
||||||
|
db.commit()
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✅ | Вы успешно изменили ссылку на товар на {editbuytovartovar}")
|
||||||
|
elif call.data == 'editbuynewtovartovarno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили изменение сcылки товара")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'editbuynewpricetovaryes' or call.data == 'editbuynewpricetovarno')
|
||||||
|
def editbuy_price_new_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'editbuynewpricetovaryes':
|
||||||
|
sql.execute(f"SELECT * FROM shop WHERE id = {editbuypriceidtovar}")
|
||||||
|
sql.execute(f"UPDATE shop SET price = {editbuypricetovar} WHERE id = {editbuypriceidtovar}")
|
||||||
|
db.commit()
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✅ | Вы успешно изменили цену товара на {editbuypricetovar}")
|
||||||
|
elif call.data == 'editbuynewpricetovarno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили изменение цены товара")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'editbuynewnametovaryes' or call.data == 'editbuynewnametovarno')
|
||||||
|
def editbuy_name_new_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'editbuynewnametovaryes':
|
||||||
|
sql.execute(f"SELECT * FROM shop WHERE id = {editbuynameidtovar}")
|
||||||
|
sql.execute(f"UPDATE shop SET name = '{editbuynametovar}' WHERE id = {editbuynameidtovar}")
|
||||||
|
db.commit()
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✅ | Вы успешно изменили название товара на {editbuynametovar}")
|
||||||
|
elif call.data == 'editbuynewnametovarno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили изменение названия товара")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'editbuyname' or call.data == 'editbuyprice' or call.data == 'editbuytovar')
|
||||||
|
def editbuy_first_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'editbuyname':
|
||||||
|
msg = client.send_message(call.message.chat.id, f"*Введите ID товара которому хотите изменить название:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, editbuy_name)
|
||||||
|
elif call.data == 'editbuyprice':
|
||||||
|
msg = client.send_message(call.message.chat.id, f"*Введите ID товара которому хотите изменить цену:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, editbuy_price)
|
||||||
|
elif call.data == 'editbuytovar':
|
||||||
|
msg = client.send_message(call.message.chat.id, f"*Введите ID товара которому хотите изменить ссылку:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, editbuy_tovar)
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['rembuy'])
|
||||||
|
def removebuy(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
accessquery = 1
|
||||||
|
with lock:
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
if getaccess < 1:
|
||||||
|
client.send_message(cid, '⚠️ | У вас нет доступа!')
|
||||||
|
else:
|
||||||
|
msg = client.send_message(cid, f"*Введите ID товара который хотите удалить:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, removebuy_next)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def removebuy_next(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global removeidtovar
|
||||||
|
removeidtovar = int(message.text)
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop WHERE id = {removeidtovar}"):
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅',callback_data='removebuytovaryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌',callback_data='removebuytovarno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"🔰 | Данные об удалении:\n\nID товара: {infoshop[0]}\nИмя товара: {infoshop[1]}\nЦена товара: {infoshop[2]}\nТовар: {infoshop[3]}\n\nВы действительно хотите удалить товар? Отменить действие будет НЕВОЗМОЖНО.",reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'removebuytovaryes' or call.data == 'removebuytovarno')
|
||||||
|
def removebuy_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'removebuytovaryes':
|
||||||
|
sql.execute(f"SELECT * FROM shop")
|
||||||
|
sql.execute(f"DELETE FROM shop WHERE id = {removeidtovar}")
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✅ | Вы успешно удалили товар")
|
||||||
|
db.commit()
|
||||||
|
elif call.data == 'removebuytovarno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили удаление товара")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['addbuy'])
|
||||||
|
def addbuy(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
with lock:
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
if getaccess < 1:
|
||||||
|
client.send_message(cid, '⚠️ | У вас нет доступа!')
|
||||||
|
else:
|
||||||
|
msg = client.send_message(cid, '*Введите ID товара:*',parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, addbuy_id)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def addbuy_id(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global addbuyid
|
||||||
|
addbuyid = message.text
|
||||||
|
msg = client.send_message(cid, '*Введите цену товара:*',parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, addbuy_price)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def addbuy_price(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global addbuyprice
|
||||||
|
addbuyprice = message.text
|
||||||
|
msg = client.send_message(cid, '*Введите название товара:*',parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, addbuy_name)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def addbuy_name(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global addbuyname
|
||||||
|
addbuyname = message.text
|
||||||
|
msg = client.send_message(cid, '*Введите ссылку на товар:*',parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, addbuy_result)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def addbuy_result(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global addbuytovar
|
||||||
|
addbuytovar = message.text
|
||||||
|
sql.execute(f"SELECT name FROM shop WHERE name = '{addbuyname}'")
|
||||||
|
if sql.fetchone() is None:
|
||||||
|
sql.execute(f"INSERT INTO shop VALUES ({addbuyid}, '{addbuyname}', {addbuyprice}, '{addbuytovar}', '')")
|
||||||
|
db.commit()
|
||||||
|
sql.execute(f"SELECT * FROM shop WHERE name = '{addbuyname}'")
|
||||||
|
client.send_message(cid, f'✅ | Вы успешно добавили товар\nID товара: {sql.fetchone()[0]}\nИмя: {addbuyname}\nЦена: {addbuyprice}\nСсылка на товар: {addbuytovar}')
|
||||||
|
else:
|
||||||
|
client.send_message(cid, f"⛔️ | Данный товар уже добавлен!")
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['buy'])
|
||||||
|
def buy(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
|
||||||
|
text = '🛒 | *Список товаров*\n\n'
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop"):
|
||||||
|
text += f"{infoshop[0]}. {infoshop[1]}\nЦена: {infoshop[2]}\n\n"
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅', callback_data='firstbuytovaryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌', callback_data='firstbuytovarno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f'{text}*Вы хотите перейти к покупке товара?*',parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def buy_next(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global tovarid
|
||||||
|
tovarid = int(message.text)
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop WHERE id = {tovarid}"):
|
||||||
|
if info[2] < infoshop[2]:
|
||||||
|
client.send_message(cid, '⚠️ | У вас недостаточно средств для приобретения товара!\n\nЧтобы пополнить счёт напишите /donate')
|
||||||
|
else:
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅',callback_data='buytovaryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌',callback_data='buytovarno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"💸 | Вы подверждаете покупку товара?\n\nВернуть средства за данный товар НЕВОЗМОЖНО.",reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'firstbuytovaryes' or call.data == 'firstbuytovarno')
|
||||||
|
def firstbuy_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'firstbuytovaryes':
|
||||||
|
msg = client.send_message(call.message.chat.id, f"*Введите ID товара который хотите купить:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, buy_next)
|
||||||
|
elif call.data == 'firstbuytovarno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили покупку товара")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'buytovaryes' or call.data == 'buytovarno')
|
||||||
|
def buy_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'buytovaryes':
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {call.from_user.id}"):
|
||||||
|
for infoshop in sql.execute(f"SELECT * FROM shop WHERE id = {tovarid}"):
|
||||||
|
if str(info[0]) not in infoshop[4]:
|
||||||
|
cashtovar = int(info[2] - infoshop[2])
|
||||||
|
boughttovar = int(info[4] + 1)
|
||||||
|
whobuytovarinttostr = str(info[0])
|
||||||
|
whobuytovar = str(infoshop[4] + whobuytovarinttostr + ',')
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {call.from_user.id}")
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✅ | Вы успешно купили товар\n\nНазвание товара: {infoshop[1]}\nЦена: {infoshop[2]}\n\nТовар: {infoshop[3]}\n\nСпасибо за покупку!")
|
||||||
|
sql.execute(f"UPDATE users SET cash = {cashtovar} WHERE id = {call.from_user.id}")
|
||||||
|
sql.execute(f"UPDATE users SET bought = {boughttovar} WHERE id = {call.from_user.id}")
|
||||||
|
sql.execute(f"SELECT * FROM shop WHERE id = {tovarid}")
|
||||||
|
sql.execute(f"UPDATE shop SET whobuy = '{whobuytovar}' WHERE id = {tovarid}")
|
||||||
|
db.commit()
|
||||||
|
else:
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"*⛔️ | Данный товар уже куплен!*\n\nЧтобы посмотреть список купленных товаров напишите /mybuy",parse_mode='Markdown')
|
||||||
|
elif call.data == 'buytovarno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"❌ | Вы отменили покупку товара!")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['donate'])
|
||||||
|
def donate(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
global uid
|
||||||
|
uid = message.from_user.id
|
||||||
|
msg = client.send_message(cid, f"*💰 | Введите сумму для пополнения:*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, donate_value)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def donate_value(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global donatevalue
|
||||||
|
global commentdonate
|
||||||
|
global getusername
|
||||||
|
global getuserdonateid
|
||||||
|
getusername = message.from_user.first_name
|
||||||
|
getuserdonateid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
commentdonate = sql.fetchone()[0]
|
||||||
|
donatevalue = int(message.text)
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅',callback_data='donateyes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌',callback_data='donateno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
global qiwibalancebe
|
||||||
|
qiwibalancebe = api.balance
|
||||||
|
msg = client.send_message(cid, f"🔰 | Заявка на пополнение средств успешно создана\n\nВы действительно хотите пополнить средства?",parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def donateyesoplacheno(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
removekeyboard = types.ReplyKeyboardRemove()
|
||||||
|
if message.text == '✅ Оплачено':
|
||||||
|
client.send_message(cid, f"✉️ | Ваш запрос отправлен администраторам, ожидайте одобрения и выдачи средств.",reply_markup=removekeyboard)
|
||||||
|
client.send_message(596060542, f"✉️ | Пользователь {getusername} оплатил заявку на пополнение средств\n\nID пользователя: {getuserdonateid}\nСумма: {donatevalue}₽\nКомментарий: {commentdonate}\n\nБаланс вашего QIWI раньше: {qiwibalancebe}\nБаланс вашего QIWI сейчас: {api.balance}\n\nПерепроверьте верность оплаты затем подтвердите выдачу средств.\nДля выдачи средств напишите: /giverub")
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'donateyes' or call.data == 'donateno')
|
||||||
|
def donate_result(call):
|
||||||
|
try:
|
||||||
|
removekeyboard = types.ReplyKeyboardRemove()
|
||||||
|
rmk = types.ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
rmk.add(types.KeyboardButton('✅ Оплачено'))
|
||||||
|
if call.data == 'donateyes':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
msg = client.send_message(call.message.chat.id, f"➖➖➖➖➖➖➖➖➖➖➖➖\n☎️ Кошелек для оплаты: +380661696196\n💰 Сумма: {donatevalue}₽\n💭 Комментарий: {commentdonate}\n*⚠️ВАЖНО⚠️* Комментарий и сумма должны быть *1в1*\n➖➖➖➖➖➖➖➖➖➖➖➖",parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
client.register_next_step_handler(msg, donateyesoplacheno)
|
||||||
|
elif call.data == 'donateno':
|
||||||
|
client.send_message(call.message.chat.id, f"❌ | Вы отменили заявку на пополнение средств",reply_markup=removekeyboard)
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['getcid'])
|
||||||
|
def getcid(message):
|
||||||
|
client.send_message(message.chat.id, f"ID чата | {message.chat.id}\nТвой ID | {message.from_user.id}")
|
||||||
|
|
||||||
|
@client.message_handler(commands=['help'])
|
||||||
|
def helpcmd(message):
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
with lock:
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
if getaccess >= 1:
|
||||||
|
client.send_message(cid, '*Помощь по командам:*\n\n/profile - Посмотреть свой профиль\n/help - Посмотреть список команд\n/buy - Купить товар\n/donate - Пополнить счёт\n/mybuy - Посмотреть список купленных товаров\n/teh - Связаться с тех.поддержкой\n\nАдмин-команды:\n\n/getprofile - Посмотреть чужой профиль\n/access - Выдать уровень доступа\n/giverub - Выдать деньги на баланс\n/getid - Узнать айди пользователя\n/getcid - Узнать Conference ID\n/addbuy - Добавить товар на продажу\n/editbuy - Изменить данные о товаре\n/rembuy - Удалить товар\n/ot - Ответить пользователю (отправить сообщение)',parse_mode='Markdown')
|
||||||
|
else:
|
||||||
|
client.send_message(cid, '*Помощь по командам:*\n\n/profile - Посмотреть свой профиль\n/help - Посмотреть список команд\n/buy - Купить товар\n/donate - Пополнить счёт\n/mybuy - Посмотреть список купленных товаров\n/teh - Связаться с тех.поддержкой',parse_mode='Markdown')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['access', 'setaccess', 'dostup'])
|
||||||
|
def setaccess(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
accessquery = 777
|
||||||
|
if getaccess < accessquery:
|
||||||
|
client.send_message(cid, f"⚠️ | У вас нет доступа!")
|
||||||
|
else:
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
msg = client.send_message(cid, 'Введите ID пользователя:\nПример: 596060542', parse_mode="Markdown")
|
||||||
|
client.register_next_step_handler(msg, access_user_id_answer)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
def access_user_id_answer(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global usridaccess
|
||||||
|
usridaccess = message.text
|
||||||
|
rmk = types.ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
rmk.add(types.KeyboardButton('Пользователь'), types.KeyboardButton('Администратор'), types.KeyboardButton('Разработчик'))
|
||||||
|
msg = client.send_message(cid, 'Какой уровень доступа Вы хотите выдать?:', reply_markup=rmk, parse_mode="Markdown")
|
||||||
|
client.register_next_step_handler(msg, access_user_access_answer)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def access_user_access_answer(message):
|
||||||
|
try:
|
||||||
|
global accessgaved
|
||||||
|
global accessgavedname
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
access_yes = types.InlineKeyboardButton(text='✅',callback_data='setaccessyes')
|
||||||
|
access_no = types.InlineKeyboardButton(text='❌',callback_data='setaccessno')
|
||||||
|
rmk.add(access_yes, access_no)
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridaccess}"):
|
||||||
|
if message.text == "Пользователь":
|
||||||
|
accessgavedname = "Пользователь"
|
||||||
|
accessgaved = 0
|
||||||
|
elif message.text == "Администратор":
|
||||||
|
accessgavedname = "Администратор"
|
||||||
|
accessgaved = 1
|
||||||
|
elif message.text == "Разработчик":
|
||||||
|
accessgavedname = "Разработчик"
|
||||||
|
accessgaved = 777
|
||||||
|
|
||||||
|
client.send_message(cid, f'Данные для выдачи:\nID пользователя: {usridaccess} ({info[1]})\nУровень доступа: {message.text}\n\nВерно?', reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(lambda call: call.data == 'setaccessyes' or call.data == 'setaccessno')
|
||||||
|
def access_user_gave_access(call):
|
||||||
|
try:
|
||||||
|
removekeyboard = types.ReplyKeyboardRemove()
|
||||||
|
if call.data == 'setaccessyes':
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridaccess}"):
|
||||||
|
sql.execute(f"UPDATE users SET access = {accessgaved} WHERE id = {usridaccess}")
|
||||||
|
db.commit()
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f'✅ | Пользователю {info[1]} выдан уровень доступа {accessgavedname}', reply_markup=removekeyboard)
|
||||||
|
elif call.data == 'setaccessno':
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridaccess}"):
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Вы отменили выдачу уровня доступа {accessgavedname} пользователю {info[1]}', reply_markup=removekeyboard)
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['getrazrab'])
|
||||||
|
def getrazrabotchik(message):
|
||||||
|
if message.from_user.id == 596060542:
|
||||||
|
sql.execute(f"UPDATE users SET access = 777 WHERE id = 596060542")
|
||||||
|
client.send_message(message.chat.id, f"✅ | Вы выдали себе Разработчика")
|
||||||
|
db.commit()
|
||||||
|
else:
|
||||||
|
client.send_message(message.chat.id, f"⛔️ | Отказано в доступе!")
|
||||||
|
|
||||||
|
@client.message_handler(commands=['giverub', 'givedonate', 'givebal'])
|
||||||
|
def giverubles(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
accessquery = 777
|
||||||
|
if getaccess < accessquery:
|
||||||
|
client.send_message(cid, f"⚠️ | У вас нет доступа!")
|
||||||
|
else:
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {uid}"):
|
||||||
|
msg = client.send_message(cid, 'Введите ID пользователя:\nПример: 596060542', parse_mode="Markdown")
|
||||||
|
client.register_next_step_handler(msg, rubles_user_id_answer)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def rubles_user_id_answer(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global usridrubles
|
||||||
|
usridrubles = message.text
|
||||||
|
rmk = types.ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
rmk.add(types.KeyboardButton('10'), types.KeyboardButton('100'), types.KeyboardButton('1000'), types.KeyboardButton('Другая сумма'))
|
||||||
|
msg = client.send_message(cid, 'Выберите сумму для выдачи:', reply_markup=rmk, parse_mode="Markdown")
|
||||||
|
client.register_next_step_handler(msg, rubles_user_rubles_answer)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def rubles_user_rubles_answer(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
global rublesgavedvalue
|
||||||
|
removekeyboard = types.ReplyKeyboardRemove()
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
access_yes = types.InlineKeyboardButton(text='✅',callback_data='giverublesyes')
|
||||||
|
access_no = types.InlineKeyboardButton(text='❌',callback_data='giverublesno')
|
||||||
|
rmk.add(access_yes, access_no)
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridrubles}"):
|
||||||
|
if message.text == '10':
|
||||||
|
rublesgavedvalue = 10
|
||||||
|
client.send_message(cid, f'Данные для выдачи:\nID пользователя: {usridrubles} ({info[1]})\nСумма: {rublesgavedvalue}\n\nВерно?',reply_markup=rmk)
|
||||||
|
elif message.text == '100':
|
||||||
|
rublesgavedvalue = 100
|
||||||
|
client.send_message(cid, f'Данные для выдачи:\nID пользователя: {usridrubles} ({info[1]})\nСумма: {rublesgavedvalue}\n\nВерно?',reply_markup=rmk)
|
||||||
|
elif message.text == '1000':
|
||||||
|
rublesgavedvalue = 1000
|
||||||
|
client.send_message(cid, f'Данные для выдачи:\nID пользователя: {usridrubles} ({info[1]})\nСумма: {rublesgavedvalue}\n\nВерно?',reply_markup=rmk)
|
||||||
|
elif message.text == 'Другая сумма':
|
||||||
|
msg = client.send_message(cid, f"*Введите сумму для выдачи:*",parse_mode='Markdown',reply_markup=removekeyboard)
|
||||||
|
client.register_next_step_handler(msg, rubles_user_rubles_answer_other)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def rubles_user_rubles_answer_other(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
global rublesgavedvalue
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
access_yes = types.InlineKeyboardButton(text='✅',callback_data='giverublesyes')
|
||||||
|
access_no = types.InlineKeyboardButton(text='❌',callback_data='giverublesno')
|
||||||
|
rmk.add(access_yes, access_no)
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridrubles}"):
|
||||||
|
if message.text == message.text:
|
||||||
|
rublesgavedvalue = int(message.text)
|
||||||
|
client.send_message(cid, f'Данные для выдачи:\nID пользователя: {usridrubles} ({info[1]})\nСумма: {rublesgavedvalue}\n\nВерно?',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(func=lambda call: call.data == 'giverublesyes' or call.data == 'giverublesno')
|
||||||
|
def rubles_gave_rubles_user(call):
|
||||||
|
try:
|
||||||
|
removekeyboard = types.ReplyKeyboardRemove()
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridrubles}"):
|
||||||
|
rubless = int(info[2] + rublesgavedvalue)
|
||||||
|
if call.data == 'giverublesyes':
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridrubles}"):
|
||||||
|
sql.execute(f"UPDATE users SET cash = {rubless} WHERE id = {usridrubles}")
|
||||||
|
db.commit()
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f'✅ | Пользователю {info[1]} выдано {rublesgavedvalue} рублей', reply_markup=removekeyboard)
|
||||||
|
elif call.data == 'giverublesno':
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {usridrubles}"):
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Вы отменили выдачу рублей пользователю {info[1]}', reply_markup=removekeyboard)
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['teh'])
|
||||||
|
def teh(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
msg = client.send_message(cid, f"*📨 | Введите текст который хотите отправить тех.поддержке*",parse_mode='Markdown')
|
||||||
|
client.register_next_step_handler(msg, teh_next)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def teh_next(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
global tehtextbyuser
|
||||||
|
global tehnamebyuser
|
||||||
|
global tehidbyuser
|
||||||
|
tehidbyuser = int(message.from_user.id)
|
||||||
|
tehnamebyuser = str(message.from_user.first_name)
|
||||||
|
tehtextbyuser = str(message.text)
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✉️',callback_data='tehsend')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌',callback_data='tehno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"✉️ | Данные об отправке:\n\nТекст для отправки: {tehtextbyuser}\n\nВы действительно хотите отправить это тех.поддержке?",parse_mode='Markdown',reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(func=lambda call: call.data == 'tehsend' or call.data == 'tehno')
|
||||||
|
def teh_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'tehsend':
|
||||||
|
for info in sql.execute(f"SELECT * FROM users WHERE id = {call.from_user.id}"):
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✉️ | Ваше сообщение отправлено тех.поддержке, ожидайте ответа.")
|
||||||
|
client.send_message(596060542, f"✉️ | Пользователь {tehnamebyuser} отправил сообщение в тех.поддержку\n\nID пользователя: {tehidbyuser}\nТекст: {tehtextbyuser}\n\nЧтобы ответить пользователю напишите /ot")
|
||||||
|
elif call.data == 'tehno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили отправку сообщения тех.поддержке")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['ot'])
|
||||||
|
def sendmsgtouser(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
|
||||||
|
msg = client.send_message(cid, f"👤 | Введите ID пользователя которому хотите отправить сообщение:")
|
||||||
|
client.register_next_step_handler(msg, sendmsgtouser_next)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def sendmsgtouser_next(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
|
||||||
|
if message.text == message.text:
|
||||||
|
global getsendmsgtouserid
|
||||||
|
getsendmsgtouserid = int(message.text)
|
||||||
|
msg = client.send_message(cid, f"📨 | Введите текст который хотите отправить пользователю:")
|
||||||
|
client.register_next_step_handler(msg, sendmsgtouser_next_text)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def sendmsgtouser_next_text(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
|
||||||
|
if message.text == message.text:
|
||||||
|
global getsendmsgtousertext
|
||||||
|
getsendmsgtousertext = str(message.text)
|
||||||
|
rmk = types.InlineKeyboardMarkup()
|
||||||
|
item_yes = types.InlineKeyboardButton(text='✅',callback_data='sendmsgtouseryes')
|
||||||
|
item_no = types.InlineKeyboardButton(text='❌',callback_data='sendmsgtouserno')
|
||||||
|
rmk.add(item_yes, item_no)
|
||||||
|
msg = client.send_message(cid, f"🔰 | Данные об отправке сообщения:\n\nID пользователя: {getsendmsgtouserid}\nТекст для отправки: {getsendmsgtousertext}\n\nОтправить сообщение?",reply_markup=rmk)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.callback_query_handler(func=lambda call: call.data == 'sendmsgtouseryes' or call.data == 'sendmsgtouserno')
|
||||||
|
def sendmsgtouser_callback(call):
|
||||||
|
try:
|
||||||
|
if call.data == 'sendmsgtouseryes':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"✉️ | Сообщение отправлено!")
|
||||||
|
client.send_message(getsendmsgtouserid, f"✉️ | Администратор прислал вам сообщение:\n\n{getsendmsgtousertext}")
|
||||||
|
elif call.data == 'sendmsgtouserno':
|
||||||
|
client.delete_message(call.message.chat.id, call.message.message_id-0)
|
||||||
|
client.send_message(call.message.chat.id, f"🚫 | Вы отменили отправку сообщения пользователю")
|
||||||
|
client.answer_callback_query(callback_query_id=call.id)
|
||||||
|
except:
|
||||||
|
client.send_message(call.message.chat.id, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
@client.message_handler(commands=['getid'])
|
||||||
|
def getiduser(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE id = {uid}")
|
||||||
|
getaccess = sql.fetchone()[3]
|
||||||
|
accessquery = 1
|
||||||
|
if getaccess < accessquery:
|
||||||
|
client.send_message(cid, f"⚠️ | У вас нет доступа!")
|
||||||
|
else:
|
||||||
|
msg = client.send_message(cid, 'Введите никнейм пользователя:')
|
||||||
|
client.register_next_step_handler(msg, next_getiduser_name)
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
def next_getiduser_name(message):
|
||||||
|
try:
|
||||||
|
cid = message.chat.id
|
||||||
|
uid = message.from_user.id
|
||||||
|
if message.text == message.text:
|
||||||
|
getusername = message.text
|
||||||
|
sql.execute(f"SELECT * FROM users WHERE nick = '{getusername}'")
|
||||||
|
result = sql.fetchone()[0]
|
||||||
|
client.send_message(cid, f'👤 | ID пользователя: {result}')
|
||||||
|
except:
|
||||||
|
client.send_message(cid, f'🚫 | Ошибка при выполнении команды')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
client.polling(none_stop=True,interval=0)
|
||||||
6
doners/shopbot/configure.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
config = {
|
||||||
|
'name': '',
|
||||||
|
'token': '',
|
||||||
|
'tokenqiwi': '',
|
||||||
|
'phoneqiwi': ''
|
||||||
|
}
|
||||||
2
doners/telegram-shop-bot-template/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
152
doners/telegram-shop-bot-template/.gitignore
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
21
doners/telegram-shop-bot-template/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 ssayand
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
47
doners/telegram-shop-bot-template/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
i dont like this project tbh :\
|
||||||
|
|
||||||
|
# Telegram shop bot template
|
||||||
|

|
||||||
|

|
||||||
|
[](https://codecov.io/gh/anekobtw/telegram-shop-bot-template)
|
||||||
|

|
||||||
|
|
||||||
|
Customizable telegram shop bot
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
Clone the project to your local machine.
|
||||||
|
```console
|
||||||
|
$ git clone https://github.com/anekobtw/telegram-shop-bot-template.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setting up
|
||||||
|
Do not modify any files except `config.py`. Doing so may result in numerous bugs :)
|
||||||
|
|
||||||
|
- Replace `BOT_TOKEN` with your Telegram bot token (Obtain it from [BotFather](https://web.telegram.org/k/#@BotFather)).
|
||||||
|
```python
|
||||||
|
BOT_TOKEN = '' #replace with your token
|
||||||
|
```
|
||||||
|
|
||||||
|
- Change the currency you're using for selling items. (optional)
|
||||||
|
```python
|
||||||
|
currency = '$'
|
||||||
|
```
|
||||||
|
|
||||||
|
- Furthermore, add the products you're selling into the dictionary using the following format:\
|
||||||
|
`'product name' - price`
|
||||||
|
|
||||||
|
```python
|
||||||
|
items = {
|
||||||
|
'Item1': 100,
|
||||||
|
'Item2': 200,
|
||||||
|
'Item3': 500
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember to restart the bot to apply the changes.
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
- [aiogram](https://github.com/aiogram/aiogram) - A modern and fully asynchronous framework for Telegram Bot API written in Python using asyncio
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
Contributions are always welcome! If you have any suggestions, feature requests, or bug reports, please feel free to open an issue on the [GitHub repository](https://github.com/anekobtw/timewise).
|
||||||
17
doners/telegram-shop-bot-template/src/config.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Insert here your bot token here
|
||||||
|
BOT_TOKEN = ""
|
||||||
|
|
||||||
|
# Enter here the shop owners' telegram ids (@getmyid_bot may help you)
|
||||||
|
# You may add as much as you can, but I recommend not to add too much.
|
||||||
|
admins = {1718021890}
|
||||||
|
|
||||||
|
currency = "$"
|
||||||
|
|
||||||
|
items = {
|
||||||
|
"tissue box": 10,
|
||||||
|
"magnet": 20,
|
||||||
|
"table": 50,
|
||||||
|
"glow stick": 30,
|
||||||
|
"bottle cap": 20,
|
||||||
|
"toothpaste": 15
|
||||||
|
}
|
||||||
77
doners/telegram-shop-bot-template/src/database.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class Order:
|
||||||
|
def __init__(self, order_id, tgid, tg_nickname, product, is_open, timestamp) -> None:
|
||||||
|
"""Initialize an order object with provided attributes"""
|
||||||
|
self.order_id = order_id
|
||||||
|
self.tgid = tgid
|
||||||
|
self.tg_nickname = tg_nickname
|
||||||
|
self.product = product
|
||||||
|
self.is_open = is_open
|
||||||
|
self.timestamp = timestamp
|
||||||
|
|
||||||
|
@property
|
||||||
|
def formatted_timestamp(self) -> str:
|
||||||
|
"""Return the timestamp in a formatted string (YYYY-MM-DD HH:MM:SS)"""
|
||||||
|
return datetime.fromtimestamp(self.timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
def all_data(self) -> str:
|
||||||
|
return f"""Order ID: {self.order_id}
|
||||||
|
Telegram ID: {self.tgid}
|
||||||
|
Telegram Nickname: {self.tg_nickname}
|
||||||
|
Product: {self.product}
|
||||||
|
Date and Time of Purchase: {self.formatted_timestamp}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class OrderManager:
|
||||||
|
DATABASE_FILE = "database.db"
|
||||||
|
TIMEZONE_OFFSET = 0 # Assuming offset in seconds
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.connection = sqlite3.connect(self.DATABASE_FILE)
|
||||||
|
self.cursor = self.connection.cursor()
|
||||||
|
|
||||||
|
self.cursor.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS orders (
|
||||||
|
order_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
tgid INT,
|
||||||
|
tg_nickname TEXT,
|
||||||
|
product TEXT,
|
||||||
|
is_open INTEGER,
|
||||||
|
timestamp DATETIME
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.connection.commit()
|
||||||
|
|
||||||
|
def get_active_orders(self) -> list:
|
||||||
|
"""Retrieve all orders that are not completed from the database"""
|
||||||
|
self.cursor.execute("SELECT * FROM orders WHERE is_open = 1")
|
||||||
|
return self.cursor.fetchall()
|
||||||
|
|
||||||
|
def get_user_orders(self, tgid: int) -> list:
|
||||||
|
"""Get the information about all user's orders"""
|
||||||
|
self.cursor.execute("SELECT * FROM orders WHERE tgid = ?", (tgid,))
|
||||||
|
return self.cursor.fetchall()
|
||||||
|
|
||||||
|
def get_order(self, order_id: int) -> int:
|
||||||
|
"""Get the order info"""
|
||||||
|
self.cursor.execute("SELECT * FROM orders WHERE order_id = ?", (order_id,))
|
||||||
|
return self.cursor.fetchone()
|
||||||
|
|
||||||
|
def insert_order(self, tgid: int, tg_nickname: str, product: str) -> None:
|
||||||
|
"""Insert a new order into the database with the provided details"""
|
||||||
|
timestamp = round(time.time()) + self.TIMEZONE_OFFSET
|
||||||
|
values = (tgid, tg_nickname, product, 1, timestamp)
|
||||||
|
self.cursor.execute("INSERT INTO orders(tgid, tg_nickname, product, is_open, timestamp) VALUES (?, ?, ?, ?, ?)", values)
|
||||||
|
self.connection.commit()
|
||||||
|
|
||||||
|
def delete_order(self, order_id: int) -> None:
|
||||||
|
"""Delete the order"""
|
||||||
|
self.cursor.execute("DELETE FROM orders WHERE order_id = ?", (order_id,))
|
||||||
|
self.connection.commit()
|
||||||
124
doners/telegram-shop-bot-template/src/main.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import config
|
||||||
|
import database
|
||||||
|
from aiogram import Bot, Dispatcher, types
|
||||||
|
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
|
from aiogram.utils import executor
|
||||||
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
|
||||||
|
# <<< Constants >>>
|
||||||
|
admins = config.admins
|
||||||
|
order_manager = database.OrderManager()
|
||||||
|
order_cb = CallbackData("order", "action", "item")
|
||||||
|
|
||||||
|
|
||||||
|
# <<< Initializing >>>
|
||||||
|
bot = Bot(token=config.BOT_TOKEN)
|
||||||
|
dp = Dispatcher(bot)
|
||||||
|
logging.basicConfig(level=logging.INFO, filename="log.txt")
|
||||||
|
|
||||||
|
|
||||||
|
async def notify_admins_order(order_id: int) -> None:
|
||||||
|
for admin_id in admins:
|
||||||
|
await bot.send_message(chat_id=admin_id, text=f"❗ Check out a new order!\nOrder id: {order_id}\n")
|
||||||
|
|
||||||
|
|
||||||
|
# <<< Inline start buttons >>>
|
||||||
|
@dp.message_handler(commands=["start"])
|
||||||
|
async def start(message: types.Message):
|
||||||
|
keyboard = InlineKeyboardMarkup()
|
||||||
|
keyboard.add(
|
||||||
|
InlineKeyboardButton(text="Check the shop", callback_data="items"),
|
||||||
|
InlineKeyboardButton(text="My orders", callback_data="my_orders"),
|
||||||
|
InlineKeyboardButton(text="Developer", callback_data="developer"),
|
||||||
|
)
|
||||||
|
|
||||||
|
await message.answer(f"Hi!, {message.from_user.mention}!", reply_markup=keyboard)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(lambda c: c.data == "items")
|
||||||
|
async def process_callback_items(callback_query: types.CallbackQuery):
|
||||||
|
await callback_query.answer()
|
||||||
|
await items(callback_query.message)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(lambda c: c.data == "my_orders")
|
||||||
|
async def process_callback_my_orders(callback_query: types.CallbackQuery):
|
||||||
|
await callback_query.answer()
|
||||||
|
orders_data = order_manager.get_user_orders(callback_query.from_user.id)
|
||||||
|
if orders_data:
|
||||||
|
orders_text = "\n".join(database.Order(*order_data).all_data() for order_data in orders_data)
|
||||||
|
await bot.send_message(chat_id=callback_query.from_user.id, text=orders_text)
|
||||||
|
else:
|
||||||
|
await bot.send_message(chat_id=callback_query.from_user.id, text="You don't have any orders yet.")
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(lambda c: c.data == "developer")
|
||||||
|
async def process_callback_developer(callback_query: types.CallbackQuery):
|
||||||
|
await bot.send_message(
|
||||||
|
chat_id=callback_query.from_user.id,
|
||||||
|
text="""Bot developer is @anekobtw
|
||||||
|
Telegram channel: @anekobtww
|
||||||
|
Github: https://github.com/anekobtw
|
||||||
|
Source code of this bot: https://github.com/anekobtw/telegram-shop-bot""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# <<< Items >>>
|
||||||
|
@dp.message_handler(commands=["items"])
|
||||||
|
async def items(message: types.Message):
|
||||||
|
keyboard = InlineKeyboardMarkup()
|
||||||
|
for item, price in config.items.items():
|
||||||
|
keyboard.add(InlineKeyboardButton(text=f"{item} - {price}{config.currency}", callback_data=order_cb.new(action="create", item=item)))
|
||||||
|
|
||||||
|
await message.answer(text="Choose the product to buy:", reply_markup=keyboard)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query_handler(order_cb.filter(action="create"))
|
||||||
|
async def order_cb_handler(query: types.CallbackQuery, callback_data: dict):
|
||||||
|
item = callback_data["item"]
|
||||||
|
order_manager.insert_order(query.from_user.id, query.from_user.full_name, item)
|
||||||
|
order_id = database.Order(*order_manager.get_active_orders()[-1]).order_id
|
||||||
|
|
||||||
|
text = f"""You\'ve bought {item} that costs {config.items[item]}{config.currency}
|
||||||
|
Order_id: {order_id}
|
||||||
|
However, if it was an accident, you may delete your purchase by simply typing /delete_order {order_id}"""
|
||||||
|
await bot.send_message(chat_id=query.from_user.id, text=text)
|
||||||
|
await notify_admins_order(order_id=order_id)
|
||||||
|
|
||||||
|
|
||||||
|
# <<< Admin commands >>>
|
||||||
|
@dp.message_handler(commands=["orders"])
|
||||||
|
async def orders(message: types.Message):
|
||||||
|
if message.from_user.id in admins:
|
||||||
|
orders_data = order_manager.get_active_orders()
|
||||||
|
for order_data in orders_data:
|
||||||
|
order_obj = database.Order(*order_data)
|
||||||
|
await message.answer(order_obj.all_data())
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(commands=["next"])
|
||||||
|
async def next1(message: types.Message):
|
||||||
|
orders_data = order_manager.get_active_orders()
|
||||||
|
order_obj = database.Order(*orders_data[0])
|
||||||
|
await message.answer(order_obj.all_data())
|
||||||
|
|
||||||
|
|
||||||
|
# <<< Others >>>
|
||||||
|
@dp.message_handler(commands=["delete_order"])
|
||||||
|
async def delete_order(message: types.Message):
|
||||||
|
try:
|
||||||
|
args = message.get_args()
|
||||||
|
order_id = int(args.split()[0])
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
await message.answer("The command is used incorrectly. Please, provide me with the order id. Example:\n/delete_order 4 ")
|
||||||
|
else:
|
||||||
|
order_data = order_manager.get_order(order_id)
|
||||||
|
if database.Order(*order_data).tgid == message.from_user.id:
|
||||||
|
order_manager.delete_order(order_id=order_id)
|
||||||
|
await message.answer(f"Order {order_id} was deleted successfully.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
executor.start_polling(dp)
|
||||||
1
doners/telegram-shop-bot-template/src/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
aiogram==2.23.1
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from aiogram import Dispatcher
|
||||||
|
|
||||||
|
def register_handlers(dp: Dispatcher):
|
||||||
|
from . import start, admin, polls
|
||||||
|
start.register_handlers(dp)
|
||||||
|
admin.register_handlers(dp)
|
||||||
|
polls.register_handlers(dp)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from aiogram import types, Dispatcher
|
||||||
|
from config import load_config
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
async def admin_command(message: types.Message):
|
||||||
|
if message.from_user.id not in config['ADMINS']:
|
||||||
|
await message.reply("Вы не являетесь админом!")
|
||||||
|
return
|
||||||
|
await message.reply("Приветствую, повелитель!")
|
||||||
|
|
||||||
|
def register_handlers(dp: Dispatcher):
|
||||||
|
dp.register_message_handler(admin_command, commands=['admin'])
|
||||||
|
|||||||