refactor: remove Alembic migrations and Discord integration

This commit is contained in:
2026-07-15 15:55:25 +05:00
parent e8cff0f7ed
commit ec5ae395f3
24 changed files with 559 additions and 1718 deletions
+35 -10
View File
@@ -28,16 +28,31 @@ class Settings(BaseSettings):
default=True,
validation_alias="BOT_DATABASE_EXPORT",
)
status_notification: bool = Field(default=True, validation_alias="BOT_STATUS_NOTIFICATION")
status_notification: bool = Field(
default=True, validation_alias="BOT_STATUS_NOTIFICATION"
)
timezone: str = Field(default="Europe/Moscow", validation_alias="BOT_TIMEZONE")
database_path: str = Field(default="tgbot/data/database.db", validation_alias="PATH_DATABASE")
database_path: str = Field(
default="tgbot/data/database.db", validation_alias="PATH_DATABASE"
)
logs_path: str = Field(default="tgbot/data/logs.log", validation_alias="PATH_LOGS")
user_cache_ttl: int = Field(default=300, ge=0, validation_alias="BOT_USER_CACHE_TTL")
throttle_rate: float = Field(default=0.5, ge=0, validation_alias="BOT_THROTTLE_RATE")
user_cache_ttl: int = Field(
default=300, ge=0, validation_alias="BOT_USER_CACHE_TTL"
)
throttle_rate: float = Field(
default=0.5, ge=0, validation_alias="BOT_THROTTLE_RATE"
)
yoomoney_client_id: str = Field(default="", validation_alias="YOOMONEY_CLIENT_ID")
# Очистка строковых значений из окружения
@field_validator("bot_token", "timezone", "database_path", "logs_path", "yoomoney_client_id", mode="before")
@field_validator(
"bot_token",
"timezone",
"database_path",
"logs_path",
"yoomoney_client_id",
mode="before",
)
@classmethod
def _strip_string(cls, value: object) -> str:
return str(value or "").strip()
@@ -52,11 +67,15 @@ class Settings(BaseSettings):
if isinstance(value, int):
values = [value]
elif isinstance(value, str):
values = [admin_id for admin_id in value.replace(" ", "").split(",") if admin_id]
values = [
admin_id for admin_id in value.replace(" ", "").split(",") if admin_id
]
elif isinstance(value, (list, tuple, set)):
values = list(value)
else:
raise ValueError("BOT_ADMIN_IDS должен быть числом или списком чисел через запятую")
raise ValueError(
"BOT_ADMIN_IDS должен быть числом или списком чисел через запятую"
)
admin_ids = []
@@ -64,10 +83,14 @@ class Settings(BaseSettings):
try:
parsed_id = int(admin_id)
except (TypeError, ValueError) as error:
raise ValueError("BOT_ADMIN_IDS должен содержать только Телеграм ID через запятую") from error
raise ValueError(
"BOT_ADMIN_IDS должен содержать только Телеграм ID через запятую"
) from error
if parsed_id <= 0:
raise ValueError("BOT_ADMIN_IDS должен содержать Телеграм ID больше нуля")
raise ValueError(
"BOT_ADMIN_IDS должен содержать Телеграм ID больше нуля"
)
admin_ids.append(parsed_id)
@@ -80,7 +103,9 @@ class Settings(BaseSettings):
try:
timezone(value)
except UnknownTimeZoneError as error:
raise ValueError("BOT_TIMEZONE должен быть корректной временной зоной, например Europe/Moscow") from error
raise ValueError(
"BOT_TIMEZONE должен быть корректной временной зоной, например Europe/Moscow"
) from error
return value
+23 -17
View File
@@ -13,14 +13,17 @@ from tgbot.utils.const_functions import get_unix
class PositionModel(Base):
__tablename__ = "storage_position"
increment: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
increment: Mapped[int] = mapped_column(
Integer, primary_key=True, autoincrement=True
)
category_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
position_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
position_name: Mapped[str] = mapped_column(String(255), nullable=False)
position_price: Mapped[float] = mapped_column(Float, nullable=False, default=0)
position_desc: Mapped[str] = mapped_column(Text, nullable=False, default="None")
position_photo: Mapped[str] = mapped_column(Text, nullable=False, default="None")
position_unix: Mapped[int] = mapped_column(Integer, nullable=False, default=get_unix)
position_unix: Mapped[int] = mapped_column(
Integer, nullable=False, default=get_unix
)
ModelBase = Position
@@ -37,13 +40,12 @@ class Positionx(BaseRepository[PositionModel, Position]):
# Добавление позиции товара
async def add(
self,
category_id: int,
position_id: int,
position_name: str,
position_price: float,
position_desc: str,
position_photo: str,
self,
category_id: int,
position_id: int,
position_name: str,
position_price: float,
position_desc: str,
) -> Position:
return await self._insert(
category_id=category_id,
@@ -51,12 +53,13 @@ class Positionx(BaseRepository[PositionModel, Position]):
position_name=position_name,
position_price=position_price,
position_desc=position_desc,
position_photo=position_photo,
position_unix=get_unix(),
)
# Обновление позиции по ID или фильтру
async def update(self, where: Optional[Union[Dict[str, Any], int]] = None, **kwargs) -> int:
async def update(
self, where: Optional[Union[Dict[str, Any], int]] = None, **kwargs
) -> int:
if isinstance(where, int):
where = {"position_id": where}
@@ -68,12 +71,15 @@ class Positionx(BaseRepository[PositionModel, Position]):
from tgbot.database.db_item import ItemModel
item_table = ItemModel.__table__
statement = (
select(item_table.c.position_id, func.count(item_table.c.increment).label("item_count"))
.group_by(item_table.c.position_id)
)
statement = select(
item_table.c.position_id,
func.count(item_table.c.increment).label("item_count"),
).group_by(item_table.c.position_id)
async with session_scope() as session:
rows = await session.execute(statement)
return {int(position_id): int(item_count) for position_id, item_count in rows.all()}
return {
int(position_id): int(item_count)
for position_id, item_count in rows.all()
}
+24 -10
View File
@@ -14,20 +14,34 @@ class SettingsModel(Base):
id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1)
status_work: Mapped[str] = mapped_column(String(16), nullable=False, default="True")
status_refill: Mapped[str] = mapped_column(String(16), nullable=False, default="False")
status_refill: Mapped[str] = mapped_column(
String(16), nullable=False, default="False"
)
status_buy: Mapped[str] = mapped_column(String(16), nullable=False, default="False")
notification_refill: Mapped[str] = mapped_column(String(16), nullable=False, default="True")
notification_buy: Mapped[str] = mapped_column(String(16), nullable=False, default="False")
notification_refill: Mapped[str] = mapped_column(
String(16), nullable=False, default="True"
)
notification_buy: Mapped[str] = mapped_column(
String(16), nullable=False, default="False"
)
misc_faq: Mapped[str] = mapped_column(String, nullable=False, default="None")
misc_support: Mapped[str] = mapped_column(String, nullable=False, default="None")
misc_bot: Mapped[str] = mapped_column(String(255), nullable=False, default="None")
misc_hosting_text: Mapped[str] = mapped_column(String(64), nullable=False, default="telegraph")
misc_token_telegraph: Mapped[str] = mapped_column(String, nullable=False, default="None")
misc_discord_webhook_url: Mapped[str] = mapped_column(String, nullable=False, default="None")
misc_discord_webhook_name: Mapped[str] = mapped_column(String(255), nullable=False, default="None")
misc_hide_category: Mapped[str] = mapped_column(String(16), nullable=False, default="False")
misc_hide_position: Mapped[str] = mapped_column(String(16), nullable=False, default="False")
misc_method_prod: Mapped[str] = mapped_column(String(16), nullable=False, default="skip")
misc_hosting_text: Mapped[str] = mapped_column(
String(64), nullable=False, default="telegraph"
)
misc_token_telegraph: Mapped[str] = mapped_column(
String, nullable=False, default="None"
)
misc_hide_category: Mapped[str] = mapped_column(
String(16), nullable=False, default="False"
)
misc_hide_position: Mapped[str] = mapped_column(
String(16), nullable=False, default="False"
)
misc_method_prod: Mapped[str] = mapped_column(
String(16), nullable=False, default="skip"
)
misc_profit_day: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
misc_profit_week: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
misc_profit_month: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
-3
View File
@@ -18,7 +18,6 @@ class Position:
position_name: str
position_price: float
position_desc: str
position_photo: str
position_unix: int
@@ -86,8 +85,6 @@ class Settings:
misc_bot: str
misc_hosting_text: str
misc_token_telegraph: str
misc_discord_webhook_url: str
misc_discord_webhook_name: str
misc_hide_category: str
misc_hide_position: str
misc_method_prod: str
-2
View File
@@ -12,13 +12,11 @@ from tgbot.database.core import database_url
PROJECT_ROOT = Path(__file__).resolve().parents[2]
ALEMBIC_INI = PROJECT_ROOT / "alembic.ini"
MIGRATIONS_DIR = PROJECT_ROOT / "migrations"
# Сбор Alembic-конфига от корня проекта
def get_alembic_config(url: str = database_url) -> Config:
config = Config(str(ALEMBIC_INI))
config.set_main_option("script_location", str(MIGRATIONS_DIR))
config.set_main_option("sqlalchemy.url", url)
return config
+1 -2
View File
@@ -6,7 +6,6 @@ from sqlalchemy import delete as sqlalchemy_delete
from sqlalchemy import select
from sqlalchemy import update as sqlalchemy_update
from tgbot.database.core import Base, database_path, session_scope
from tgbot.database.migration_runner import run_migrations
from tgbot.utils.misc.bot_logging import bot_logger
ModelTranslator = TypeVar("ModelTranslator", bound=Base)
@@ -151,7 +150,7 @@ class BaseRepository(Generic[ModelTranslator, EntityTranslator]):
# Применение миграций и создание дефолтных строк
async def prepare_database() -> None:
database_path.parent.mkdir(parents=True, exist_ok=True)
await run_migrations()
# await run_migrations()
from tgbot.database.db_payments import Paymentsx
from tgbot.database.db_settings import Settingsx
+51 -32
View File
@@ -73,9 +73,15 @@ async def payment_cryptobot_finl() -> InlineKeyboardMarkup:
assets_symbol = ""
if get_payments.status_cryptobot == "True":
status_kb = ikb(f"{assets_symbol} | Статус: Включено ✅", data="payment_cryptobot_status:False")
status_kb = ikb(
f"{assets_symbol} | Статус: Включено ✅",
data="payment_cryptobot_status:False",
)
else:
status_kb = ikb(f"{assets_symbol} | Статус: Выключено ❌", data="payment_cryptobot_status:True")
status_kb = ikb(
f"{assets_symbol} | Статус: Выключено ❌",
data="payment_cryptobot_status:True",
)
keyboard.row(
ikb("Информация ♻️", data="payment_cryptobot_check"),
@@ -104,9 +110,15 @@ async def payment_yoomoney_finl() -> InlineKeyboardMarkup:
assets_symbol = ""
if get_payments.status_yoomoney == "True":
status_kb = ikb(f"{assets_symbol} | Статус: Включено ✅", data="payment_yoomoney_status:False")
status_kb = ikb(
f"{assets_symbol} | Статус: Включено ✅",
data="payment_yoomoney_status:False",
)
else:
status_kb = ikb(f"{assets_symbol} | Статус: Выключено ❌", data="payment_yoomoney_status:True")
status_kb = ikb(
f"{assets_symbol} | Статус: Выключено ❌",
data="payment_yoomoney_status:True",
)
keyboard.row(
ikb("Информация ♻️", data="payment_yoomoney_check"),
@@ -132,9 +144,13 @@ async def payment_stars_finl() -> InlineKeyboardMarkup:
assets_symbol = ""
if get_payments.status_stars == "True":
status_kb = ikb(f"{assets_symbol} | Статус: Включено ✅", data="payment_stars_status:False")
status_kb = ikb(
f"{assets_symbol} | Статус: Включено ✅", data="payment_stars_status:False"
)
else:
status_kb = ikb(f"{assets_symbol} | Статус: Выключено ❌", data="payment_stars_status:True")
status_kb = ikb(
f"{assets_symbol} | Статус: Выключено ❌", data="payment_stars_status:True"
)
keyboard.row(
ikb("Информация ♻️", data="payment_stars_check"),
@@ -169,16 +185,9 @@ async def settings_finl() -> InlineKeyboardMarkup:
if get_settings.misc_support == "None":
support_kb = ikb("Не установлена ❌", data="settings_edit_support")
else:
support_kb = ikb(f"@{get_settings.misc_support}", data="settings_edit_support")
# Вебхук дискорда
if get_settings.misc_discord_webhook_url == "None":
discord_webhook_kb = ikb("Отсутствует ❌", data="settings_edit_discord_webhook")
else:
discord_webhook_kb = ikb(f"{get_settings.misc_discord_webhook_name}", data="settings_edit_discord_webhook")
# Текстовый хостинг по умолчанию
hosting_text_default_kb = ikb(get_settings.misc_hosting_text.title(), data="settings_edit_hosting_text")
support_kb = ikb(
f"@{get_settings.misc_support}", data="settings_edit_support"
)
# Скрытие категорий без товаров
if get_settings.misc_hide_category == "True":
@@ -199,19 +208,20 @@ async def settings_finl() -> InlineKeyboardMarkup:
method_prod_kb = ikb("На каждой строке", data="settings_edit_method_prod:skip")
keyboard.row(
ikb("❔ FAQ", data="..."), faq_kb,
ikb("❔ FAQ", data="..."),
faq_kb,
).row(
ikb("☎️ Поддержка", data="..."), support_kb,
ikb("☎️ Поддержка", data="..."),
support_kb,
).row(
ikb("🧿 Дискорд Webhook", url="https://teletype.in/@djimbox/djimboshop-discord"), discord_webhook_kb,
ikb("🎁 Категории без товаров", data="..."),
hide_category_kb,
).row(
ikb("🗯 Текстовый хостинг", data="..."), hosting_text_default_kb,
ikb("🎁 Позиции без товаров", data="..."),
hide_position_kb,
).row(
ikb("🎁 Категории без товаров", data="..."), hide_category_kb,
).row(
ikb("🎁 Позиции без товаров", data="..."), hide_position_kb,
).row(
ikb("🎁 Метод добавления", data="..."), method_prod_kb,
ikb("🎁 Метод добавления", data="..."),
method_prod_kb,
)
return keyboard.as_markup()
@@ -227,7 +237,9 @@ async def settings_status_finl() -> InlineKeyboardMarkup:
status_buy_kb = ikb("Включены ✅", data="settings_status_buy:False")
status_refill_kb = ikb("Включены ✅", data="settings_status_refill:False")
notification_buy_kb = ikb("Включены 🔔", data="settings_notification_buy:False")
notification_refill_kb = ikb("Включены 🔔", data="settings_notification_refill:False")
notification_refill_kb = ikb(
"Включены 🔔", data="settings_notification_refill:False"
)
if get_settings.status_buy == "False":
status_buy_kb = ikb("Выключены ❌", data="settings_status_buy:True")
@@ -238,18 +250,25 @@ async def settings_status_finl() -> InlineKeyboardMarkup:
if get_settings.notification_buy == "False":
notification_buy_kb = ikb("Выключены 🔕", data="settings_notification_buy:True")
if get_settings.notification_refill == "False":
notification_refill_kb = ikb("Выключены 🔕", data="settings_notification_refill:True")
notification_refill_kb = ikb(
"Выключены 🔕", data="settings_notification_refill:True"
)
keyboard.row(
ikb("⛔ Тех. работы", data="..."), status_work_kb,
ikb("⛔ Тех. работы", data="..."),
status_work_kb,
).row(
ikb("💰 Пополнения", data="..."), status_refill_kb,
ikb("💰 Пополнения", data="..."),
status_refill_kb,
).row(
ikb("🎁 Покупки", data="..."), status_buy_kb,
ikb("🎁 Покупки", data="..."),
status_buy_kb,
).row(
ikb("📢 Увед. о покупках", data="..."), notification_buy_kb,
ikb("📢 Увед. о покупках", data="..."),
notification_buy_kb,
).row(
ikb("📢 Увед. о пополнениях", data="..."), notification_refill_kb,
ikb("📢 Увед. о пополнениях", data="..."),
notification_refill_kb,
)
return keyboard.as_markup()
+36 -15
View File
@@ -10,7 +10,9 @@ from tgbot.utils.const_functions import ikb
################################################################################
################################### КАТЕГОРИИ ##################################
# Изменение категории
async def category_edit_open_finl(bot: Bot, category_id: int, remover: int) -> InlineKeyboardMarkup:
async def category_edit_open_finl(
bot: Bot, category_id: int, remover: int
) -> InlineKeyboardMarkup:
keyboard = InlineKeyboardBuilder()
get_bot = await bot.get_me()
@@ -19,7 +21,10 @@ async def category_edit_open_finl(bot: Bot, category_id: int, remover: int) -> I
ikb("▪️ Изм. Название", data=f"category_edit_name:{category_id}:{remover}"),
ikb("▪️ Добавить позицию", data=f"position_add_open:{category_id}"),
).row(
ikb("▪️ Скопировать ссылку", copy=f"t.me/{get_bot.username}?start=c_{category_id}"),
ikb(
"▪️ Скопировать ссылку",
copy=f"t.me/{get_bot.username}?start=c_{category_id}",
),
ikb("▪️ Удалить", data=f"category_edit_delete:{category_id}:{remover}"),
).row(
ikb("🔙 Вернуться", data=f"category_edit_swipe:{remover}"),
@@ -34,8 +39,11 @@ def category_edit_delete_finl(category_id: int, remover: int) -> InlineKeyboardM
keyboard = InlineKeyboardBuilder()
keyboard.row(
ikb("✅ Да, удалить", data=f"category_edit_delete_confirm:{category_id}:{remover}"),
ikb("❌ Нет, отменить", data=f"category_edit_open:{category_id}:{remover}")
ikb(
"✅ Да, удалить",
data=f"category_edit_delete_confirm:{category_id}:{remover}",
),
ikb("❌ Нет, отменить", data=f"category_edit_open:{category_id}:{remover}"),
)
return keyboard.as_markup()
@@ -55,7 +63,9 @@ def category_edit_cancel_finl(category_id: int, remover: int) -> InlineKeyboardM
################################################################################
#################################### ПОЗИЦИИ ###################################
# Кнопки при открытии позиции для изменения
async def position_edit_open_finl(bot: Bot, position_id: int, remover: int) -> InlineKeyboardMarkup:
async def position_edit_open_finl(
bot: Bot, position_id: int, remover: int
) -> InlineKeyboardMarkup:
keyboard = InlineKeyboardBuilder()
get_position = await Positionx().get_required(position_id=position_id)
@@ -66,7 +76,6 @@ async def position_edit_open_finl(bot: Bot, position_id: int, remover: int) -> I
ikb("▪️ Изм. Цену", data=f"position_edit_price:{position_id}:{remover}"),
).row(
ikb("▪️ Изм. Описание", data=f"position_edit_desc:{position_id}:{remover}"),
ikb("▪️ Изм. Фото", data=f"position_edit_photo:{position_id}:{remover}"),
).row(
ikb("▪️ Добавить Товары", data=f"item_add_position_open:{position_id}"),
ikb("▪️ Выгрузить Товары", data=f"position_edit_items:{position_id}:{remover}"),
@@ -74,10 +83,16 @@ async def position_edit_open_finl(bot: Bot, position_id: int, remover: int) -> I
ikb("▪️ Очистить Товары", data=f"position_edit_clear:{position_id}:{remover}"),
ikb("▪️ Удалить Товар", data=f"item_delete_swipe:{position_id}:0"),
).row(
ikb("▪️ Скопировать ссылку", copy=f"t.me/{get_bot.username}?start=p_{position_id}"),
ikb(
"▪️ Скопировать ссылку",
copy=f"t.me/{get_bot.username}?start=p_{position_id}",
),
ikb("▪️ Удалить Позицию", data=f"position_edit_delete:{position_id}:{remover}"),
).row(
ikb("🔙 Вернуться", data=f"position_edit_swipe:{get_position.category_id}:{remover}"),
ikb(
"🔙 Вернуться",
data=f"position_edit_swipe:{get_position.category_id}:{remover}",
),
ikb("▪️ Обновить", data=f"position_edit_open:{position_id}:{remover}"),
)
@@ -89,8 +104,11 @@ def position_edit_delete_finl(position_id: int, remover: int) -> InlineKeyboardM
keyboard = InlineKeyboardBuilder()
keyboard.row(
ikb("✅ Да, удалить", data=f"position_edit_delete_confirm:{position_id}:{remover}"),
ikb("❌ Нет, отменить", data=f"position_edit_open:{position_id}:{remover}")
ikb(
"✅ Да, удалить",
data=f"position_edit_delete_confirm:{position_id}:{remover}",
),
ikb("❌ Нет, отменить", data=f"position_edit_open:{position_id}:{remover}"),
)
return keyboard.as_markup()
@@ -101,8 +119,11 @@ def position_edit_clear_finl(position_id: int, remover: int) -> InlineKeyboardMa
keyboard = InlineKeyboardBuilder()
keyboard.row(
ikb("✅ Да, очистить", data=f"position_edit_clear_confirm:{position_id}:{remover}"),
ikb("❌ Нет, отменить", data=f"position_edit_open:{position_id}:{remover}")
ikb(
"✅ Да, очистить",
data=f"position_edit_clear_confirm:{position_id}:{remover}",
),
ikb("❌ Нет, отменить", data=f"position_edit_open:{position_id}:{remover}"),
)
return keyboard.as_markup()
@@ -168,7 +189,7 @@ def products_removes_categories_finl() -> InlineKeyboardMarkup:
keyboard.row(
ikb("✅ Да, удалить все", data="prod_removes_categories_confirm"),
ikb("❌ Нет, отменить", data="prod_removes_return")
ikb("❌ Нет, отменить", data="prod_removes_return"),
)
return keyboard.as_markup()
@@ -180,7 +201,7 @@ def products_removes_positions_finl() -> InlineKeyboardMarkup:
keyboard.row(
ikb("✅ Да, удалить все", data="prod_removes_positions_confirm"),
ikb("❌ Нет, отменить", data="prod_removes_return")
ikb("❌ Нет, отменить", data="prod_removes_return"),
)
return keyboard.as_markup()
@@ -192,7 +213,7 @@ def products_removes_items_finl() -> InlineKeyboardMarkup:
keyboard.row(
ikb("✅ Да, удалить все", data="prod_removes_items_confirm"),
ikb("❌ Нет, отменить", data="prod_removes_return")
ikb("❌ Нет, отменить", data="prod_removes_return"),
)
return keyboard.as_markup()
+176 -149
View File
@@ -26,12 +26,23 @@ from tgbot.keyboards.inline_admin_products import (
products_removes_items_finl,
item_add_finish_finl,
)
from tgbot.services.api_discord import DiscordAPI
from tgbot.services.api_hosting_text import HostingAPI
from tgbot.utils.const_functions import clear_list, is_number, to_number, del_message, ded, clear_html, gen_id
from tgbot.utils.const_functions import (
clear_list,
is_number,
to_number,
del_message,
ded,
clear_html,
gen_id,
)
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc.bot_models import FSM, ARS
from tgbot.utils.text_functions import category_open_admin, position_open_admin, item_open_admin
from tgbot.utils.text_functions import (
category_open_admin,
position_open_admin,
item_open_admin,
)
router = Router(name=__name__)
@@ -123,8 +134,10 @@ async def prod_removes(message: Message, bot: Bot, state: FSM, arSession: ARS):
################################################################################
############################### СОЗДАНИЕ КАТЕГОРИИ #############################
# Принятие названия категории для её создания
@router.message(F.text, StateFilter('here_category_name'))
async def prod_category_add_name_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
@router.message(F.text, StateFilter("here_category_name"))
async def prod_category_add_name_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
if len(message.text) > 50:
return await message.answer(
"<b>❌ Название не может превышать 50 символов</b>\n"
@@ -143,7 +156,9 @@ async def prod_category_add_name_get(message: Message, bot: Bot, state: FSM, arS
############################### ИЗМЕНЕНИЕ КАТЕГОРИИ ############################
# Страница выбора категорий для редактирования
@router.callback_query(F.data.startswith("category_edit_swipe:"))
async def prod_category_edit_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_category_edit_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
remover = int(call.data.split(":")[1])
await call.message.edit_text(
@@ -154,7 +169,9 @@ async def prod_category_edit_swipe(call: CallbackQuery, bot: Bot, state: FSM, ar
# Выбор текущей категории для редактирования
@router.callback_query(F.data.startswith("category_edit_open:"))
async def prod_category_edit_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_category_edit_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -167,7 +184,9 @@ async def prod_category_edit_open(call: CallbackQuery, bot: Bot, state: FSM, arS
############################ САМО ИЗМЕНЕНИЕ КАТЕГОРИИ ##########################
# Изменение названия категории
@router.callback_query(F.data.startswith("category_edit_name:"))
async def prod_category_edit_name(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_category_edit_name(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -184,10 +203,12 @@ async def prod_category_edit_name(call: CallbackQuery, bot: Bot, state: FSM, arS
# Принятие нового названия для категории
@router.message(F.text, StateFilter('here_category_edit_name'))
async def prod_category_edit_name_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
category_id = (await state.get_data())['here_category_id']
remover = (await state.get_data())['here_remover']
@router.message(F.text, StateFilter("here_category_edit_name"))
async def prod_category_edit_name_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
category_id = (await state.get_data())["here_category_id"]
remover = (await state.get_data())["here_remover"]
if len(message.text) > 50:
return await message.answer(
@@ -204,7 +225,9 @@ async def prod_category_edit_name_get(message: Message, bot: Bot, state: FSM, ar
# Удаление категории
@router.callback_query(F.data.startswith("category_edit_delete:"))
async def prod_category_edit_delete(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_category_edit_delete(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -216,7 +239,9 @@ async def prod_category_edit_delete(call: CallbackQuery, bot: Bot, state: FSM, a
# Подтверждение удаления категории
@router.callback_query(F.data.startswith("category_edit_delete_confirm:"))
async def prod_category_edit_delete_confirm(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_category_edit_delete_confirm(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -241,7 +266,9 @@ async def prod_category_edit_delete_confirm(call: CallbackQuery, bot: Bot, state
############################### ДОБАВЛЕНИЕ ПОЗИЦИИ #############################
# Cтраницы выбора категорий для расположения позиции
@router.callback_query(F.data.startswith("position_add_swipe:"))
async def prod_position_add_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_add_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
remover = int(call.data.split(":")[1])
await call.message.edit_text(
@@ -252,7 +279,9 @@ async def prod_position_add_swipe(call: CallbackQuery, bot: Bot, state: FSM, arS
# Выбор категории для создания позиции
@router.callback_query(F.data.startswith("position_add_open:"))
async def prod_position_add_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_add_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
await state.update_data(here_category_id=category_id)
@@ -262,8 +291,10 @@ async def prod_position_add_open(call: CallbackQuery, bot: Bot, state: FSM, arSe
# Принятие названия для создания позиции
@router.message(F.text, StateFilter('here_position_name'))
async def prod_position_add_name_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
@router.message(F.text, StateFilter("here_position_name"))
async def prod_position_add_name_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
if len(message.text) > 50:
return await message.answer(
"<b>❌ Название не может превышать 50 символов</b>\n"
@@ -277,12 +308,13 @@ async def prod_position_add_name_get(message: Message, bot: Bot, state: FSM, arS
# Принятие цены позиции для её создания
@router.message(F.text, StateFilter('here_position_price'))
async def prod_position_add_price_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
@router.message(F.text, StateFilter("here_position_price"))
async def prod_position_add_price_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
if not is_number(message.text):
return await message.answer(
"<b>❌ Данные были введены неверно</b>\n"
"📁 Введите цену для позиции",
"<b>❌ Данные были введены неверно</b>\n" "📁 Введите цену для позиции",
)
if to_number(message.text) > 10_000_000 or to_number(message.text) < 0:
@@ -291,11 +323,10 @@ async def prod_position_add_price_get(message: Message, bot: Bot, state: FSM, ar
"📁 Введите цену для позиции",
)
category_id = (await state.get_data())['here_category_id']
position_name = (await state.get_data())['here_position_name']
category_id = (await state.get_data())["here_category_id"]
position_name = (await state.get_data())["here_position_name"]
position_price = to_number(message.text)
position_desc = "None"
position_photo = "None"
position_id = gen_id(12)
await state.clear()
@@ -305,7 +336,6 @@ async def prod_position_add_price_get(message: Message, bot: Bot, state: FSM, ar
position_name=position_name,
position_price=position_price,
position_desc=position_desc,
position_photo=position_photo,
)
await position_open_admin(bot, position_id, message.from_user.id)
@@ -315,7 +345,9 @@ async def prod_position_add_price_get(message: Message, bot: Bot, state: FSM, ar
############################ РЕДАКТИРОВАНИЕ ПОЗИЦИИ ############################
# Страницы выбора категории для редактирования позиции
@router.callback_query(F.data.startswith("position_edit_category_swipe:"))
async def prod_position_edit_category_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_category_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
remover = int(call.data.split(":")[1])
await call.message.edit_text(
@@ -326,7 +358,9 @@ async def prod_position_edit_category_swipe(call: CallbackQuery, bot: Bot, state
# Открытие категории для выбора позиции
@router.callback_query(F.data.startswith("position_edit_category_open:"))
async def prod_position_edit_category_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_category_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
get_category = await Categoryx().get_required(category_id=category_id)
@@ -338,12 +372,16 @@ async def prod_position_edit_category_open(call: CallbackQuery, bot: Bot, state:
reply_markup=await position_edit_swipe_fp(0, category_id),
)
else:
await call.answer(f"📁 Позиции в категории {get_category.category_name} отсутствуют")
await call.answer(
f"📁 Позиции в категории {get_category.category_name} отсутствуют"
)
# Страницы выбора позиции для редактирования
@router.callback_query(F.data.startswith("position_edit_swipe:"))
async def prod_position_edit_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -357,7 +395,9 @@ async def prod_position_edit_swipe(call: CallbackQuery, bot: Bot, state: FSM, ar
# Выбор позиции для редактирования
@router.callback_query(F.data.startswith("position_edit_open:"))
async def prod_position_edit_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -370,7 +410,9 @@ async def prod_position_edit_open(call: CallbackQuery, bot: Bot, state: FSM, arS
############################ САМО ИЗМЕНЕНИЕ ПОЗИЦИИ ############################
# Изменение названия позиции
@router.callback_query(F.data.startswith("position_edit_name:"))
async def prod_position_edit_name(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_name(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -386,10 +428,12 @@ async def prod_position_edit_name(call: CallbackQuery, bot: Bot, state: FSM, arS
# Принятие названия позиции для её изменения
@router.message(F.text, StateFilter('here_position_edit_name'))
async def prod_position_edit_name_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
position_id = (await state.get_data())['here_position_id']
remover = (await state.get_data())['here_remover']
@router.message(F.text, StateFilter("here_position_edit_name"))
async def prod_position_edit_name_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
position_id = (await state.get_data())["here_position_id"]
remover = (await state.get_data())["here_remover"]
if len(message.text) > 50:
return await message.answer(
@@ -406,7 +450,9 @@ async def prod_position_edit_name_get(message: Message, bot: Bot, state: FSM, ar
# Изменение цены позиции
@router.callback_query(F.data.startswith("position_edit_price:"))
async def prod_position_edit_price(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_price(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -422,10 +468,12 @@ async def prod_position_edit_price(call: CallbackQuery, bot: Bot, state: FSM, ar
# Принятие цены позиции для её изменения
@router.message(F.text, StateFilter('here_position_edit_price'))
async def prod_position_edit_price_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
position_id = (await state.get_data())['here_position_id']
remover = (await state.get_data())['here_remover']
@router.message(F.text, StateFilter("here_position_edit_price"))
async def prod_position_edit_price_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
position_id = (await state.get_data())["here_position_id"]
remover = (await state.get_data())["here_remover"]
if not is_number(message.text):
return await message.answer(
@@ -449,7 +497,9 @@ async def prod_position_edit_price_get(message: Message, bot: Bot, state: FSM, a
# Изменение описания позиции
@router.callback_query(F.data.startswith("position_edit_desc:"))
async def prod_position_edit_desc(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_desc(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -469,10 +519,12 @@ async def prod_position_edit_desc(call: CallbackQuery, bot: Bot, state: FSM, arS
# Принятие описания позиции для её изменения
@router.message(F.text, StateFilter('here_position_edit_desc'))
async def prod_position_edit_desc_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
position_id = (await state.get_data())['here_position_id']
remover = (await state.get_data())['here_remover']
@router.message(F.text, StateFilter("here_position_edit_desc"))
async def prod_position_edit_desc_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
position_id = (await state.get_data())["here_position_id"]
remover = (await state.get_data())["here_remover"]
if len(message.text) > 1200:
return await message.answer(
@@ -510,74 +562,11 @@ async def prod_position_edit_desc_get(message: Message, bot: Bot, state: FSM, ar
await position_open_admin(bot, position_id, message.from_user.id)
# Изменение изображения позиции
@router.callback_query(F.data.startswith("position_edit_photo:"))
async def prod_position_edit_photo(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
get_settings = await Settingsx().get()
if get_settings.misc_discord_webhook_url == "None":
return await call.answer("🧿 Отсутствует Дискорд вебхук, добавьте его в настройках", True)
await state.update_data(here_position_id=position_id)
await state.update_data(here_remover=remover)
await state.set_state("here_position_edit_photo")
await del_message(call.message)
await call.message.answer(
"<b>📁 Отправьте новое изображение для позиции</b>\n"
"❕ Отправьте <code>0</code> чтобы пропустить.",
reply_markup=position_edit_cancel_finl(position_id, remover),
)
# Принятие нового фото для позиции
@router.message((F.text == "0") | F.photo, StateFilter('here_position_edit_photo'))
async def prod_position_edit_photo_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
position_id = (await state.get_data())['here_position_id']
remover = (await state.get_data())['here_remover']
get_settings = await Settingsx().get()
if get_settings.misc_discord_webhook_url == "None":
return await message.answer(
"<b>🧿 Отсутствует Дискорд вебхук, добавьте его в настройках</b>"
)
position_photo = "None"
if message.photo is not None:
cache_message = await message.answer(
"<b>♻️ Подождите, фотография загружается...</b>"
)
file_path = (await bot.get_file(message.photo[-1].file_id)).file_path
photo_path = await bot.download_file(file_path)
pay_image_status, pay_image_url = await (
await DiscordAPI.connect(
bot=bot,
arSession=arSession,
update=message,
)
).upload_photo(photo_path.read())
if pay_image_status:
position_photo = pay_image_url
await del_message(cache_message)
await state.clear()
await Positionx().update(position_id, position_photo=position_photo)
await position_open_admin(bot, position_id, message.from_user.id)
# Выгрузка товаров
@router.callback_query(F.data.startswith("position_edit_items:"))
async def prod_position_edit_items(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_items(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -607,7 +596,9 @@ async def prod_position_edit_items(call: CallbackQuery, bot: Bot, state: FSM, ar
# Удаление позиции
@router.callback_query(F.data.startswith("position_edit_delete:"))
async def prod_position_edit_delete(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_delete(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -621,7 +612,9 @@ async def prod_position_edit_delete(call: CallbackQuery, bot: Bot, state: FSM, a
# Подтверждение удаления позиции
@router.callback_query(F.data.startswith("position_edit_delete_confirm:"))
async def prod_position_edit_delete_confirm(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_delete_confirm(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -636,7 +629,9 @@ async def prod_position_edit_delete_confirm(call: CallbackQuery, bot: Bot, state
if len(get_positions) >= 1:
await call.message.edit_text(
"<b>📁 Выберите позицию для изменения 🖍</b>",
reply_markup=await position_edit_swipe_fp(remover, get_position.category_id),
reply_markup=await position_edit_swipe_fp(
remover, get_position.category_id
),
)
else:
await del_message(call.message)
@@ -644,7 +639,9 @@ async def prod_position_edit_delete_confirm(call: CallbackQuery, bot: Bot, state
# Очистка позиции
@router.callback_query(F.data.startswith("position_edit_clear:"))
async def prod_position_edit_clear(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_clear(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -657,7 +654,9 @@ async def prod_position_edit_clear(call: CallbackQuery, bot: Bot, state: FSM, ar
# Согласие на очистку позиции
@router.callback_query(F.data.startswith("position_edit_clear_confirm:"))
async def prod_position_edit_clear_confirm(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_edit_clear_confirm(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -672,7 +671,9 @@ async def prod_position_edit_clear_confirm(call: CallbackQuery, bot: Bot, state:
############################### ДОБАВЛЕНИЕ ТОВАРОВ #############################
# Страницы выбора категории для добавления товара
@router.callback_query(F.data.startswith("item_add_category_swipe:"))
async def prod_item_add_category_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_item_add_category_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
remover = int(call.data.split(":")[1])
await call.message.edit_text(
@@ -683,7 +684,9 @@ async def prod_item_add_category_swipe(call: CallbackQuery, bot: Bot, state: FSM
# Открытие категории для выбора позиции
@router.callback_query(F.data.startswith("item_add_category_open:"))
async def prod_item_add_category_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_item_add_category_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -698,12 +701,16 @@ async def prod_item_add_category_open(call: CallbackQuery, bot: Bot, state: FSM,
reply_markup=await item_add_position_swipe_fp(0, category_id),
)
else:
await call.answer(f"🎁 Позиции в категории {get_category.category_name} отсутствуют")
await call.answer(
f"🎁 Позиции в категории {get_category.category_name} отсутствуют"
)
# Страницы выбора позиции для добавления товара
@router.callback_query(F.data.startswith("item_add_position_swipe:"))
async def prod_item_add_position_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_item_add_position_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
category_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -714,8 +721,10 @@ async def prod_item_add_position_swipe(call: CallbackQuery, bot: Bot, state: FSM
# Выбор позиции для добавления товаров
@router.callback_query(F.data.startswith("item_add_position_open:"), flags={'rate': 0})
async def prod_item_add_position_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
@router.callback_query(F.data.startswith("item_add_position_open:"), flags={"rate": 0})
async def prod_item_add_position_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
get_position = await Positionx().get_required(position_id=position_id)
@@ -734,9 +743,9 @@ async def prod_item_add_position_open(call: CallbackQuery, bot: Bot, state: FSM,
<b>🎁 Отправляйте данные товаров</b>
❗ Товары разделяются одной пустой строчкой. Пример:
<code>Данные товара...
Данные товара...
Данные товара...</code>
"""),
reply_markup=item_add_finish_finl(position_id),
@@ -755,12 +764,16 @@ async def prod_item_add_position_open(call: CallbackQuery, bot: Bot, state: FSM,
# Завершение загрузки товаров
@router.callback_query(F.data.startswith('item_add_position_finish:'), flags={'rate': 0})
async def prod_item_add_finish(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
@router.callback_query(
F.data.startswith("item_add_position_finish:"), flags={"rate": 0}
)
async def prod_item_add_finish(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
try:
count_items = (await state.get_data())['here_add_item_count']
count_items = (await state.get_data())["here_add_item_count"]
except Exception:
bot_logger.debug("В state нет счетчика добавленных товаров", exc_info=True)
count_items = 0
@@ -777,7 +790,7 @@ async def prod_item_add_finish(call: CallbackQuery, bot: Bot, state: FSM, arSess
# Принятие данных товара
@router.message(F.text, StateFilter('here_add_items'), flags={'rate': 0})
@router.message(F.text, StateFilter("here_add_items"), flags={"rate": 0})
async def prod_item_add_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
cache_message = await message.answer("<b>⌛ Ждите, товары добавляются..</b>")
@@ -788,9 +801,9 @@ async def prod_item_add_get(message: Message, bot: Bot, state: FSM, arSession: A
else:
get_items = clear_list(message.text.split("\n"))
item_count = (await state.get_data())['here_add_item_count']
category_id = (await state.get_data())['here_add_item_category_id']
position_id = (await state.get_data())['here_add_item_position_id']
item_count = (await state.get_data())["here_add_item_count"]
category_id = (await state.get_data())["here_add_item_category_id"]
position_id = (await state.get_data())["here_add_item_position_id"]
await state.update_data(here_add_item_count=item_count + len(get_items))
@@ -811,7 +824,9 @@ async def prod_item_add_get(message: Message, bot: Bot, state: FSM, arSession: A
############################### УДАЛЕНИЕ ТОВАРОВ ###############################
# Страницы удаления товаров
@router.callback_query(F.data.startswith("item_delete_swipe:"))
async def prod_item_delete_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_item_delete_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
position_id = int(call.data.split(":")[1])
remover = int(call.data.split(":")[2])
@@ -826,12 +841,16 @@ async def prod_item_delete_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSe
reply_markup=await item_delete_swipe_fp(remover, position_id),
)
else:
await call.answer(f"🎁 Товары в позиции {get_position.position_name} отсутствуют", True)
await call.answer(
f"🎁 Товары в позиции {get_position.position_name} отсутствуют", True
)
# Удаление товара
@router.callback_query(F.data.startswith("item_delete_open:"))
async def prod_item_delete_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_item_delete_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
item_id = int(call.data.split(":")[1])
await del_message(call.message)
@@ -840,7 +859,9 @@ async def prod_item_delete_open(call: CallbackQuery, bot: Bot, state: FSM, arSes
# Подтверждение удаления товара
@router.callback_query(F.data.startswith("item_delete_confirm:"))
async def prod_item_delete_confirm_open(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_item_delete_confirm_open(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
item_id = int(call.data.split(":")[1])
get_item = await Itemx().get_required(item_id=item_id)
@@ -862,7 +883,9 @@ async def prod_item_delete_confirm_open(call: CallbackQuery, bot: Bot, state: FS
############################### УДАЛЕНИЕ РАЗДЕЛОВ ##############################
# Возвращение к меню удаления разделов
@router.callback_query(F.data == "prod_removes_return")
async def prod_removes_return(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_removes_return(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
await state.clear()
await call.message.edit_text(
@@ -873,7 +896,9 @@ async def prod_removes_return(call: CallbackQuery, bot: Bot, state: FSM, arSessi
# Удаление всех категорий
@router.callback_query(F.data == "prod_removes_categories")
async def prod_removes_categories(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_removes_categories(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_categories = len(await Categoryx().get_all())
get_positions = len(await Positionx().get_all())
get_items = len(await Itemx().get_all())
@@ -891,7 +916,9 @@ async def prod_removes_categories(call: CallbackQuery, bot: Bot, state: FSM, arS
# Подтверждение удаления всех категорий (позиций и товаров включительно)
@router.callback_query(F.data == "prod_removes_categories_confirm")
async def prod_removes_categories_confirm(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_removes_categories_confirm(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_categories = len(await Categoryx().get_all())
get_positions = len(await Positionx().get_all())
get_items = len(await Itemx().get_all())
@@ -900,19 +927,19 @@ async def prod_removes_categories_confirm(call: CallbackQuery, bot: Bot, state:
await Positionx().clear()
await Itemx().clear()
await call.message.edit_text(
ded(f"""
await call.message.edit_text(ded(f"""
<b>✅ Вы успешно удалили все категории</b>
🗃 Категорий: <code>{get_categories}шт</code>
📁 Позиций: <code>{get_positions}шт</code>
🎁 Товаров: <code>{get_items}шт</code>
""")
)
"""))
# Удаление всех позиций
@router.callback_query(F.data == "prod_removes_positions")
async def prod_removes_positions(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_removes_positions(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_positions = len(await Positionx().get_all())
get_items = len(await Itemx().get_all())
@@ -928,20 +955,20 @@ async def prod_removes_positions(call: CallbackQuery, bot: Bot, state: FSM, arSe
# Подтверждение удаления всех позиций (товаров включительно)
@router.callback_query(F.data == "prod_removes_positions_confirm")
async def prod_position_remove(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def prod_position_remove(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_positions = len(await Positionx().get_all())
get_items = len(await Itemx().get_all())
await Positionx().clear()
await Itemx().clear()
await call.message.edit_text(
ded(f"""
await call.message.edit_text(ded(f"""
<b>✅ Вы успешно удалили все позиции</b>
📁 Позиций: <code>{get_positions}шт</code>
🎁 Товаров: <code>{get_items}шт</code>
""")
)
"""))
# Удаление всех товаров
+33 -104
View File
@@ -5,7 +5,6 @@ from aiogram.types import CallbackQuery, Message
from tgbot.database import Settingsx, Userx
from tgbot.keyboards.inline_admin import settings_status_finl, settings_finl
from tgbot.services.api_discord import DiscordDJ, DiscordAPI
from tgbot.utils.const_functions import ded
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc.bot_models import FSM, ARS
@@ -40,7 +39,9 @@ async def settings_status_edit(message: Message, bot: Bot, state: FSM, arSession
################################## ВЫКЛЮЧАТЕЛИ #################################
# Включение/выключение тех работ
@router.callback_query(F.data.startswith("settings_status_work:"))
async def settings_status_work(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_status_work(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
get_user = await Userx().get_required(user_id=call.from_user.id)
@@ -65,7 +66,9 @@ async def settings_status_work(call: CallbackQuery, bot: Bot, state: FSM, arSess
# Включение/выключение покупок
@router.callback_query(F.data.startswith("settings_status_buy:"))
async def settings_status_buy(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_status_buy(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
get_user = await Userx().get_required(user_id=call.from_user.id)
@@ -90,7 +93,9 @@ async def settings_status_buy(call: CallbackQuery, bot: Bot, state: FSM, arSessi
# Включение/выключение пополнений
@router.callback_query(F.data.startswith("settings_status_refill:"))
async def settings_status_refill(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_status_refill(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
get_user = await Userx().get_required(user_id=call.from_user.id)
@@ -113,7 +118,9 @@ async def settings_status_refill(call: CallbackQuery, bot: Bot, state: FSM, arSe
# Включение/выключение уведомлений о покупках
@router.callback_query(F.data.startswith("settings_notification_buy:"))
async def settings_notification_buy(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_notification_buy(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
get_user = await Userx().get_required(user_id=call.from_user.id)
@@ -136,7 +143,9 @@ async def settings_notification_buy(call: CallbackQuery, bot: Bot, state: FSM, a
# Включение/выключение уведомлений о пополнениях
@router.callback_query(F.data.startswith("settings_notification_refill:"))
async def settings_notification_refill(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_notification_refill(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
get_user = await Userx().get_required(user_id=call.from_user.id)
@@ -165,20 +174,20 @@ async def settings_faq_edit(call: CallbackQuery, bot: Bot, state: FSM, arSession
await state.clear()
await state.set_state("here_settings_faq")
await call.message.edit_text(
ded("""
await call.message.edit_text(ded("""
<b>❔ Введите новый текст для FAQ</b>
❕ Вы можете использовать заготовленный синтаксис и HTML разметку:
▪️ <code>{username}</code> - логин пользоваля
▪️ <code>{user_id}</code> - айди пользователя
▪️ <code>{firstname}</code> - имя пользователя
""")
)
"""))
# Изменение поддержки
@router.callback_query(F.data == "settings_edit_support")
async def settings_support_edit(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_support_edit(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
await state.clear()
await state.set_state("here_settings_support")
@@ -190,7 +199,9 @@ async def settings_support_edit(call: CallbackQuery, bot: Bot, state: FSM, arSes
# Изменение отображения/скрытия категорий без товаров
@router.callback_query(F.data.startswith("settings_edit_hide_category:"))
async def settings_edit_hide_category(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_edit_hide_category(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
status = call.data.split(":")[1]
await Settingsx().update(misc_hide_category=status)
@@ -203,7 +214,9 @@ async def settings_edit_hide_category(call: CallbackQuery, bot: Bot, state: FSM,
# Изменение отображения/скрытия позиций без товаров
@router.callback_query(F.data.startswith("settings_edit_hide_position:"))
async def settings_edit_hide_position(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_edit_hide_position(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
status = call.data.split(":")[1]
await Settingsx().update(misc_hide_position=status)
@@ -216,7 +229,9 @@ async def settings_edit_hide_position(call: CallbackQuery, bot: Bot, state: FSM,
# Изменение метода добавления товаров
@router.callback_query(F.data.startswith("settings_edit_method_prod:"))
async def settings_edit_method_prod(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_edit_method_prod(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
method = call.data.split(":")[1]
await Settingsx().update(misc_method_prod=method)
@@ -227,32 +242,11 @@ async def settings_edit_method_prod(call: CallbackQuery, bot: Bot, state: FSM, a
)
# Изменение дискорд вебхука
@router.callback_query(F.data == "settings_edit_discord_webhook")
async def settings_discord_edit(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
await state.clear()
get_discord_public_webhook = await (
DiscordDJ(
arSession=arSession,
bot=bot,
)
).export_webhook()
await state.set_state("here_settings_discord_webhook")
await call.message.edit_text(
ded(f"""
<b>🧿 Отправьте новый вебхук дискорда</b>
❕ Для удаления вебхука введите <code>0</code>
❕ Вы можете использовать публичный вебхук, но ответственность за его использование лежит только на вас
▪️ Публичный вебхук: <code>{get_discord_public_webhook}</code>
""")
)
# Изменение текстового хостинга по умолчанию
@router.callback_query(F.data == "settings_edit_hosting_text")
async def settings_edit_hosting_text(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def settings_edit_hosting_text(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_settings = await Settingsx().get()
if get_settings.misc_hosting_text == "telegraph":
@@ -282,8 +276,7 @@ async def settings_faq_get(message: Message, bot: Bot, state: FSM, arSession: AR
except Exception:
bot_logger.debug("Некорректная HTML-разметка FAQ", exc_info=True)
return await message.answer(
"<b>❌ Ошибка синтаксиса HTML</b>\n"
"❔ Введите новый текст для FAQ",
"<b>❌ Ошибка синтаксиса HTML</b>\n" "❔ Введите новый текст для FAQ",
)
await state.clear()
@@ -310,67 +303,3 @@ async def settings_support_get(message: Message, bot: Bot, state: FSM, arSession
"<b>🖍 Изменение данных бота</b>",
reply_markup=await settings_finl(),
)
# Принятие дискорд вебхука
@router.message(F.text, StateFilter("here_settings_discord_webhook"))
async def settings_discord_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
get_discord_webhook = message.text
# Удаление вебхука
if get_discord_webhook == "0":
await Settingsx().update(
misc_discord_webhook_url="None",
misc_discord_webhook_name="None",
)
return await message.answer(
"<b>⚙️ Настройки бота</b>",
reply_markup=await settings_finl(),
)
# Добавление нового вебхука
cache_message = await message.answer("<b>♻️ Проверка дискорд вебхука..</b>")
if "api" in get_discord_webhook and "webhooks" in get_discord_webhook:
discord_webhook_status, discord_webhook_name = await (
await DiscordAPI.connect(
bot=bot,
arSession=arSession,
update=message,
webhook_url=get_discord_webhook,
skipping_error=True,
)
).check()
if discord_webhook_status:
await state.clear()
await Settingsx().update(
misc_discord_webhook_url=message.text,
misc_discord_webhook_name=discord_webhook_name,
)
return await cache_message.edit_text(
"<b>⚙️ Настройки бота</b>",
reply_markup=await settings_finl(),
)
# Обработка ошибки добавления вебхука
get_discord_public_webhook = await (
DiscordDJ(
arSession=arSession,
bot=bot,
)
).export_webhook()
await cache_message.edit_text(
ded(f"""
<b>❌ Указан некорректный вебхук</b>
➖➖➖➖➖➖➖➖➖➖
🧿 Отправьте новый вебхук дискорда
❕ Для удаления вебхука введите <code>0</code>
❕ Вы можете использовать публичный вебхук, но ответственность за его использование лежит только на вас
▪️ Публичный вебхук: <code>{get_discord_public_webhook}</code>
""")
)
+13 -25
View File
@@ -5,7 +5,7 @@ from aiogram import Router, Bot, F
from aiogram.filters import Command
from aiogram.types import CallbackQuery, Message
from tgbot.data.config import BOT_VERSION, get_text_desc, get_text_warning
from tgbot.data.config import BOT_VERSION, get_text_desc
from tgbot.database import Purchasesx, Settingsx
from tgbot.keyboards.inline_user import user_support_finl
from tgbot.keyboards.inline_user_page import *
@@ -60,7 +60,7 @@ async def user_available(message: Message, bot: Bot, state: FSM, arSession: ARS)
# Открытие FAQ
@router.message(F.text.in_(('❔ FAQ', '/faq')))
@router.message(F.text.in_(("❔ FAQ", "/faq")))
async def user_faq(message: Message, bot: Bot, state: FSM, arSession: ARS):
await state.clear()
@@ -68,15 +68,7 @@ async def user_faq(message: Message, bot: Bot, state: FSM, arSession: ARS):
if get_settings.misc_faq == "None":
return await message.answer(
ded(f"""
❔ Текст FAQ не указан. Измените его в настройках бота.
➖➖➖➖➖➖➖➖➖➖
{get_text_desc()}
➖➖➖➖➖➖➖➖➖➖
{get_text_warning()}
"""),
ded(f"""❔ Текст FAQ не указан."""),
disable_web_page_preview=True,
)
@@ -87,7 +79,7 @@ async def user_faq(message: Message, bot: Bot, state: FSM, arSession: ARS):
# Открытие сообщения с ссылкой на поддержку
@router.message(F.text.in_(('☎️ Поддержка', '/support')))
@router.message(F.text.in_(("☎️ Поддержка", "/support")))
async def user_support(message: Message, bot: Bot, state: FSM, arSession: ARS):
await state.clear()
@@ -95,15 +87,7 @@ async def user_support(message: Message, bot: Bot, state: FSM, arSession: ARS):
if get_settings.misc_support == "None":
return await message.answer(
ded(f"""
☎️ Контакты поддержки не указаны. Измените их в настройках бота.
➖➖➖➖➖➖➖➖➖➖
{get_text_desc()}
➖➖➖➖➖➖➖➖➖➖
{get_text_warning()}
"""),
ded(f"""☎️ Контакты поддержки не указаны."""),
disable_web_page_preview=True,
)
@@ -114,7 +98,7 @@ async def user_support(message: Message, bot: Bot, state: FSM, arSession: ARS):
# Получение версии бота
@router.message(Command(commands=['version']))
@router.message(Command(commands=["version"]))
async def admin_version(message: Message, bot: Bot, state: FSM, arSession: ARS):
await state.clear()
@@ -122,7 +106,7 @@ async def admin_version(message: Message, bot: Bot, state: FSM, arSession: ARS):
# Получение информации о боте
@router.message(Command(commands=['dj_desc']))
@router.message(Command(commands=["dj_desc"]))
async def admin_desc(message: Message, bot: Bot, state: FSM, arSession: ARS):
await state.clear()
@@ -132,7 +116,9 @@ async def admin_desc(message: Message, bot: Bot, state: FSM, arSession: ARS):
################################################################################
# Переход к профилю
@router.callback_query(F.data == "user_profile")
async def user_profile_return(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def user_profile_return(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
await state.clear()
await del_message(call.message)
@@ -176,7 +162,9 @@ async def user_purchases(call: CallbackQuery, bot: Bot, state: FSM, arSession: A
# Страницы наличия товаров
@router.callback_query(F.data.startswith("user_available_swipe:"))
async def user_available_swipe(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def user_available_swipe(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
remover = int(call.data.split(":")[1])
items_available, remover_max, remover_now = await get_items_available(remover)
-239
View File
@@ -1,239 +0,0 @@
# - *- coding: utf- 8 - *-
import asyncio
import json
from io import BytesIO
from typing import Any, Dict, Optional, Tuple, Union
import ujson
from aiogram import Bot
from aiogram.types import CallbackQuery, Message
from aiohttp import ClientConnectorCertificateError, FormData
from tgbot.database import Settingsx
from tgbot.utils.const_functions import gen_id, send_errors
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc.bot_models import ARS
from tgbot.utils.misc_functions import send_admins
class DiscordAPI:
# Настройка клиента Discord webhook
def __init__(
self,
bot: Bot,
arSession: ARS,
update: Optional[Union[Message, CallbackQuery]] = None,
webhook_url: str = "None",
adding: bool = False,
skipping_error: bool = False,
):
self.bot = bot
self.arSession = arSession
self.update = update
self.adding = adding
self.skipping_error = skipping_error
self.webhook_username = "Djimbo Shop | Free Bot"
self.base_url = "https://djimbo.dev/dsapi"
webhook_url = self._normalize_webhook_url(webhook_url)
if "/" in webhook_url:
self.webhook_id = webhook_url.split("/")[0]
self.webhook_token = webhook_url.split("/")[1]
else:
self.webhook_id = ""
self.webhook_token = ""
# Нормализация Discord webhook URL
@staticmethod
def _normalize_webhook_url(webhook_url: str) -> str:
if webhook_url is None:
return "None"
if webhook_url.startswith("https://discord.com/api/"):
webhook_url = webhook_url[33:]
if webhook_url.startswith("discord.com/api/webhooks/"):
webhook_url = webhook_url[25:]
return webhook_url
# Инициализация данных
@classmethod
async def connect(
cls,
bot: Bot,
arSession: ARS,
update: Optional[Union[Message, CallbackQuery]] = None,
webhook_url: Optional[str] = None,
skipping_error: bool = False,
) -> "DiscordAPI":
adding = webhook_url is not None
if webhook_url is None:
settings = await Settingsx().get()
webhook_url = settings.misc_discord_webhook_url
adding = False
return cls(
bot=bot,
arSession=arSession,
update=update,
webhook_url=webhook_url,
adding=adding,
skipping_error=skipping_error,
)
# Рассылка админам о нерабочем вебхуке
async def error_account_admin(self, error_code: str = "Unknown"):
bot_logger.warning("Discord webhook недоступен: %s", error_code)
if not self.skipping_error:
await send_admins(
self.bot,
f"<b>🧿 Дискорд вебхук недоступен. Как можно быстрее его замените</b>\n"
f"❗️ Ошибка: <code>{error_code}</code>"
)
# Проверка вебхука
async def check(self) -> Tuple[bool, str]:
request_url = f"{self.base_url}/webhooks/{self.webhook_id}/{self.webhook_token}"
status, response = await self._request(
request_url=request_url,
request_method="GET",
)
if status and "channel_id" in response:
discord_channel_id = response['channel_id']
discord_hook_name = response['name']
return True, discord_hook_name
return False, ""
# Загрузка фотографий
async def upload_photo(self, photo_data: Union[BytesIO, bytes], photo_name: Optional[str] = None) -> Tuple[
bool, str]:
request_url = f"{self.base_url}/webhooks/{self.webhook_id}/{self.webhook_token}"
if photo_name is None:
photo_name = str(gen_id(24))
if not photo_name.endswith(".png") and not photo_name.endswith(".jpg"):
photo_name = f"{photo_name}.png"
send_json = {
'username': self.webhook_username,
'content': '',
}
data = FormData()
data.add_field('file', photo_data, filename=photo_name)
data.add_field('payload_json', ujson.dumps(send_json))
await asyncio.sleep(1)
status, response = await self._request(
request_url=request_url,
request_method="POST",
request_data=data,
)
if "id" in response:
channel_id = response['channel_id']
message_id = response['id']
get_discord_forevercdn = await (
DiscordDJ(
arSession=self.arSession,
bot=self.bot,
)
).export_forevercdn()
photo_url = f"{get_discord_forevercdn}/attachments/{channel_id}/{message_id}"
return True, photo_url
return False, "None"
# Запрос
async def _request(
self,
request_url: str,
request_method: str,
request_data: Optional[Union[Dict[str, Any], FormData]] = None,
) -> Tuple[bool, Any]:
session = await self.arSession.get_session()
await asyncio.sleep(1)
try:
response = await session.request(
method=request_method,
url=request_url,
data=request_data,
headers={"Accept-Encoding": "gzip, deflate"},
)
response_data = json.loads((await response.read()).decode())
if response.status == 200:
return True, response_data
else:
await self.error_account_admin(f"{response.status} - {str(response_data)}")
return False, response_data
except ClientConnectorCertificateError:
bot_logger.warning("Ошибка SSL при запросе Discord webhook", exc_info=True)
await self.error_account_admin("CERTIFICATE_VERIFY_FAILED")
return False, "CERTIFICATE_VERIFY_FAILED"
except Exception as ex:
bot_logger.warning("Ошибка запроса Discord webhook", exc_info=True)
await self.error_account_admin(str(ex))
return False, str(ex)
# Извлечение ссылок для работы с локальным АПИ дискорда
class DiscordDJ:
# Настройка клиента сервисных ссылок Discord
def __init__(self, arSession: ARS, bot: Bot):
self.arSession = arSession
self.bot = bot
self.const_url = "https://djimbo.dev/autoshop_discord.json"
# Экспорт публичного дискорд вебхука
async def export_webhook(self) -> str:
session = await self.arSession.get_session()
try:
response = await session.get(
self.const_url,
headers={"Accept-Encoding": "gzip, deflate"},
)
response_data = json.loads((await response.read()).decode())
except Exception as ex:
await send_errors(self.bot, 7729051, f"Error getting Discord Webhook - {ex}")
return "None"
else:
return response_data['webhook']
# Экспорт ссылки на постоянный CDN
async def export_forevercdn(self) -> str:
session = await self.arSession.get_session()
try:
response = await session.get(
self.const_url,
headers={"Accept-Encoding": "gzip, deflate"},
)
response_data = json.loads((await response.read()).decode())
except Exception as ex:
await send_errors(self.bot, 7729051, f"Error getting Discord ForeverCDN - {ex}")
return "None"
else:
return response_data['forevercdn']
+36 -37
View File
@@ -29,13 +29,13 @@ def rkb(text: str) -> KeyboardButton:
# Быстрая сборка inline-кнопки
def ikb(
text: str,
data: Optional[str] = None,
url: Optional[str] = None,
switch: Optional[str] = None,
web: Optional[str] = None,
copy: Optional[str] = None,
login: Optional[str] = None,
text: str,
data: Optional[str] = None,
url: Optional[str] = None,
switch: Optional[str] = None,
web: Optional[str] = None,
copy: Optional[str] = None,
login: Optional[str] = None,
) -> InlineKeyboardButton:
if data is not None:
return InlineKeyboardButton(text=text, callback_data=data)
@@ -61,36 +61,27 @@ async def del_message(message: Message) -> None:
bot_logger.debug("Не удалось удалить сообщение", exc_info=True)
# Отправка фото или обычного сообщения
# Отправка обычного сообщения
async def smart_message(
bot: Bot,
user_id: int,
text: str,
keyboard: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
photo: Optional[str] = None,
bot: Bot,
user_id: int,
text: str,
keyboard: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
) -> None:
if photo is not None and photo.title() != "None":
await bot.send_photo(
chat_id=user_id,
photo=photo,
caption=text,
reply_markup=keyboard,
)
else:
await bot.send_message(
chat_id=user_id,
text=text,
reply_markup=keyboard,
)
await bot.send_message(
chat_id=user_id,
text=text,
reply_markup=keyboard,
)
# Отправка сообщения всем админам
async def send_admins(
bot: Bot,
text: str,
keyboard: Optional[InlineKeyboardMarkup] = None,
markup: Optional[InlineKeyboardMarkup] = None,
not_me: int = 0,
bot: Bot,
text: str,
keyboard: Optional[InlineKeyboardMarkup] = None,
markup: Optional[InlineKeyboardMarkup] = None,
not_me: int = 0,
) -> None:
reply_markup = markup or keyboard
@@ -104,7 +95,9 @@ async def send_admins(
disable_web_page_preview=True,
)
except Exception:
bot_logger.warning("Не удалось отправить сообщение админу %s", admin, exc_info=True)
bot_logger.warning(
"Не удалось отправить сообщение админу %s", admin, exc_info=True
)
# Логирование ошибки и отправка админам
@@ -120,8 +113,10 @@ def ded(get_text: str) -> str:
if get_text is not None:
split_text = get_text.split("\n")
if split_text[0] == "": split_text.pop(0)
if split_text[-1] == "": split_text.pop()
if split_text[0] == "":
split_text.pop(0)
if split_text[-1] == "":
split_text.pop()
save_text = []
for text in split_text:
@@ -163,7 +158,7 @@ def convert_list(get_lists: List[list]) -> list:
# Разделение списка на части нужного размера
def split_list(get_list: list, count: int) -> List[list]:
return [get_list[i:i + count] for i in range(0, len(get_list), count)]
return [get_list[i : i + count] for i in range(0, len(get_list), count)]
# Старое имя для разделения сообщений
@@ -198,9 +193,13 @@ def convert_date(from_time, full=True, second=True) -> Union[str, int]:
from_timestamp = int(from_time)
if full:
return datetime.fromtimestamp(from_timestamp, bot_timezone).strftime("%d.%m.%Y %H:%M:%S")
return datetime.fromtimestamp(from_timestamp, bot_timezone).strftime(
"%d.%m.%Y %H:%M:%S"
)
if second:
return datetime.fromtimestamp(from_timestamp, bot_timezone).strftime("%d.%m.%Y %H:%M")
return datetime.fromtimestamp(from_timestamp, bot_timezone).strftime(
"%d.%m.%Y %H:%M"
)
return datetime.fromtimestamp(from_timestamp, bot_timezone).strftime("%d.%m.%Y")
+30 -15
View File
@@ -7,8 +7,13 @@ from typing import Union
from aiogram import Bot
from aiogram.types import FSInputFile, CallbackQuery, Message
from tgbot.data.config import BOT_DATABASE_EXPORT, BOT_STATUS_NOTIFICATION, BOT_VERSION, PATH_DATABASE, get_admins, \
get_text_desc
from tgbot.data.config import (
BOT_DATABASE_EXPORT,
BOT_STATUS_NOTIFICATION,
BOT_VERSION,
PATH_DATABASE,
get_admins,
)
from tgbot.database import Userx, Settingsx
from tgbot.utils.const_functions import get_unix, get_date, ded, send_admins
from tgbot.utils.misc.bot_logging import bot_logger
@@ -40,9 +45,17 @@ async def autosettings_unix():
now_month = datetime.now().month
now_year = datetime.now().year
unix_day = int(datetime.strptime(f"{now_day}.{now_month}.{now_year} 0:0:0", "%d.%m.%Y %H:%M:%S").timestamp())
unix_day = int(
datetime.strptime(
f"{now_day}.{now_month}.{now_year} 0:0:0", "%d.%m.%Y %H:%M:%S"
).timestamp()
)
unix_week = unix_day - (now_week * 86400)
unix_month = int(datetime.strptime(f"1.{now_month}.{now_year} 0:0:0", "%d.%m.%Y %H:%M:%S").timestamp())
unix_month = int(
datetime.strptime(
f"1.{now_month}.{now_year} 0:0:0", "%d.%m.%Y %H:%M:%S"
).timestamp()
)
await Settingsx().update(
misc_profit_day=unix_day,
@@ -68,8 +81,6 @@ async def startup_notify(bot: Bot, arSession: ARS):
ded(f"""
<b>✅ Бот был успешно запущен</b>
➖➖➖➖➖➖➖➖➖➖
{get_text_desc()}
➖➖➖➖➖➖➖➖➖➖
<code>❗ Данное сообщение видят только администраторы бота.</code>
"""),
)
@@ -91,7 +102,9 @@ async def autobackup_admin(bot: Bot):
disable_notification=True,
)
except Exception:
bot_logger.warning("Не удалось отправить автобэкап админу %s", admin, exc_info=True)
bot_logger.warning(
"Не удалось отправить автобэкап админу %s", admin, exc_info=True
)
# Проверка наличия обновлений бота
@@ -106,7 +119,7 @@ async def check_update(bot: Bot, arSession: ARS):
response_data = json.loads((await response.read()).decode())
if float(response_data['version']) > float(BOT_VERSION):
if float(response_data["version"]) > float(BOT_VERSION):
await send_admins(
bot,
ded(f"""
@@ -132,7 +145,7 @@ async def check_mail(bot: Bot, arSession: ARS):
)
response_data = json.loads((await response.read()).decode())
if response_data['status']:
if response_data["status"]:
await send_admins(
bot,
ded(f"""
@@ -176,21 +189,23 @@ async def functions_mail_make(bot: Bot, message: Message, call: CallbackQuery):
users_receive += 1
except Exception:
users_block += 1
bot_logger.debug("Пользователь %s не получил рассылку", user.user_id, exc_info=True)
bot_logger.debug(
"Пользователь %s не получил рассылку", user.user_id, exc_info=True
)
users_count += 1
if users_count % 10 == 0:
await call.message.edit_text(f"<b>📢 Рассылка началась... ({users_count}/{len(get_users)})</b>")
await call.message.edit_text(
f"<b>📢 Рассылка началась... ({users_count}/{len(get_users)})</b>"
)
await asyncio.sleep(0.07)
await call.message.edit_text(
ded(f"""
await call.message.edit_text(ded(f"""
<b>📢 Рассылка была завершена за <code>{get_unix() - get_time}сек</code></b>
➖➖➖➖➖➖➖➖➖➖
👤 Всего пользователей: <code>{len(get_users)}</code>
✅ Пользователей получило сообщение: <code>{users_receive}</code>
❌ Пользователей не получило сообщение: <code>{users_block}</code>
""")
)
"""))
+100 -30
View File
@@ -21,7 +21,11 @@ from tgbot.database import (
ModelUser,
)
from tgbot.keyboards.inline_admin import profile_edit_finl
from tgbot.keyboards.inline_admin_products import position_edit_open_finl, category_edit_open_finl, item_delete_finl
from tgbot.keyboards.inline_admin_products import (
position_edit_open_finl,
category_edit_open_finl,
item_delete_finl,
)
from tgbot.keyboards.inline_user import user_profile_finl
from tgbot.keyboards.inline_user_products import products_open_finl
from tgbot.services.api_hosting_text import HostingAPI
@@ -68,7 +72,7 @@ async def position_open_user(bot: Bot, user_id: int, position_id: int, remover:
await bot.send_message(
chat_id=user_id,
text=ded(f"""
<b>🎁 Покупка товара</b>{hide_link(get_position.position_photo)}
<b>🎁 Покупка товара</b>
➖➖➖➖➖➖➖➖➖➖
▪️ Название: <code>{get_position.position_name}</code>
▪️ Категория: <code>{get_category.category_name}</code>
@@ -78,7 +82,6 @@ async def position_open_user(bot: Bot, user_id: int, position_id: int, remover:
"""),
link_preview_options=LinkPreviewOptions(show_above_text=True),
reply_markup=products_open_finl(position_id, get_position.category_id, remover),
)
@@ -114,7 +117,7 @@ async def open_profile_admin(bot: Bot, user_id: int, get_user: ModelUser):
async def refill_open_admin(bot: Bot, user_id: int, get_refill: ModelRefill):
get_user = await Userx().get_required(user_id=get_refill.user_id)
if get_refill.refill_method in ['Form', 'Nickname', 'Number', 'QIWI']:
if get_refill.refill_method in ["Form", "Nickname", "Number", "QIWI"]:
pay_method = "QIWI 🥝"
elif get_refill.refill_method == "Yoomoney":
pay_method = "ЮMoney 🔮"
@@ -138,7 +141,9 @@ async def refill_open_admin(bot: Bot, user_id: int, get_refill: ModelRefill):
# Открытие покупки админом
async def purchase_open_admin(bot: Bot, arSession: ARS, user_id: int, get_purchase: ModelPurchases):
async def purchase_open_admin(
bot: Bot, arSession: ARS, user_id: int, get_purchase: ModelPurchases
):
get_user = await Userx().get_required(user_id=get_purchase.user_id)
link_items = await (
@@ -172,8 +177,18 @@ async def purchase_open_admin(bot: Bot, arSession: ARS, user_id: int, get_purcha
# Открытие категории админом
async def category_open_admin(bot: Bot, user_id: int, category_id: int, remover: int):
profit_amount_all, profit_amount_day, profit_amount_week, profit_amount_month = 0, 0, 0, 0
profit_count_all, profit_count_day, profit_count_week, profit_count_month = 0, 0, 0, 0
profit_amount_all, profit_amount_day, profit_amount_week, profit_amount_month = (
0,
0,
0,
0,
)
profit_count_all, profit_count_day, profit_count_week, profit_count_month = (
0,
0,
0,
0,
)
get_items = await Itemx().gets(category_id=category_id)
get_category = await Categoryx().get_required(category_id=category_id)
@@ -217,8 +232,18 @@ async def category_open_admin(bot: Bot, user_id: int, category_id: int, remover:
# Открытие позиции админом
async def position_open_admin(bot: Bot, position_id: int, user_id: int):
profit_amount_all, profit_amount_day, profit_amount_week, profit_amount_month = 0, 0, 0, 0
profit_count_all, profit_count_day, profit_count_week, profit_count_month = 0, 0, 0, 0
profit_amount_all, profit_amount_day, profit_amount_week, profit_amount_month = (
0,
0,
0,
0,
)
profit_count_all, profit_count_day, profit_count_week, profit_count_month = (
0,
0,
0,
0,
)
get_items = await Itemx().gets(position_id=position_id)
get_position = await Positionx().get_required(position_id=position_id)
@@ -227,12 +252,6 @@ async def position_open_admin(bot: Bot, position_id: int, user_id: int):
get_settings = await Settingsx().get()
get_purchases = await Purchasesx().gets(purchase_position_id=position_id)
# Наличие фото
if get_position.position_photo != "None":
position_photo_text = "<code>Присутствует ✅</code>"
else:
position_photo_text = "<code>Отсутствует ❌</code>"
# Наличие описания
if get_position.position_desc != "None":
position_desc = f"{get_position.position_desc}"
@@ -257,14 +276,13 @@ async def position_open_admin(bot: Bot, position_id: int, user_id: int):
await bot.send_message(
chat_id=user_id,
text=ded(f"""
<b>📁 Редактирование позиции</b>{hide_link(get_position.position_photo)}
<b>📁 Редактирование позиции</b>
➖➖➖➖➖➖➖➖➖➖
▪️ Категория: <code>{get_category.category_name}</code>
▪️ Позиция: <code>{get_position.position_name}</code>
▪️ Стоимость: <code>{get_position.position_price}₽</code>
▪️ Количество: <code>{len(get_items)}шт</code>
▪️ Дата создания: <code>{convert_date(get_category.category_unix)}</code>
▪️ Изображение: {position_photo_text}
▪️ Описание: {position_desc}
💸 Продаж за День: <code>{profit_count_day}шт</code> - <code>{round(profit_amount_day, 2)}₽</code>
@@ -274,7 +292,6 @@ async def position_open_admin(bot: Bot, position_id: int, user_id: int):
"""),
link_preview_options=LinkPreviewOptions(show_above_text=True),
reply_markup=await position_edit_open_finl(bot, position_id, 0),
)
@@ -303,11 +320,38 @@ async def item_open_admin(bot: Bot, item_id: int, user_id: int):
################################################################################
# Статистика бота
async def get_statistics() -> str:
refill_amount_all, refill_amount_day, refill_amount_week, refill_amount_month = 0, 0, 0, 0
refill_count_all, refill_count_day, refill_count_week, refill_count_month = 0, 0, 0, 0
profit_amount_all, profit_amount_day, profit_amount_week, profit_amount_month = 0, 0, 0, 0
profit_count_all, profit_count_day, profit_count_week, profit_count_month = 0, 0, 0, 0
users_all, users_day, users_week, users_month, users_money_have, users_money_give = 0, 0, 0, 0, 0, 0
refill_amount_all, refill_amount_day, refill_amount_week, refill_amount_month = (
0,
0,
0,
0,
)
refill_count_all, refill_count_day, refill_count_week, refill_count_month = (
0,
0,
0,
0,
)
profit_amount_all, profit_amount_day, profit_amount_week, profit_amount_month = (
0,
0,
0,
0,
)
profit_count_all, profit_count_day, profit_count_week, profit_count_month = (
0,
0,
0,
0,
)
(
users_all,
users_day,
users_week,
users_month,
users_money_have,
users_money_give,
) = (0, 0, 0, 0, 0, 0)
refill_cryptobot_count, refill_cryptobot_amount = 0, 0
refill_yoomoney_count, refill_yoomoney_amount = 0, 0
refill_stars_count, refill_stars_amount = 0, 0
@@ -375,12 +419,28 @@ async def get_statistics() -> str:
# Даты обновления статистики
all_days = [
'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье',
"Понедельник",
"Вторник",
"Среда",
"Четверг",
"Пятница",
"Суббота",
"Воскресенье",
]
all_months = [
'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь',
'Октябрь', 'Ноябрь', 'Декабрь'
"Январь",
"Февраль",
"Март",
"Апрель",
"Май",
"Июнь",
"Июль",
"Август",
"Сентябрь",
"Октябрь",
"Ноябрь",
"Декабрь",
]
now_day = datetime.now().day
@@ -388,12 +448,22 @@ async def get_statistics() -> str:
now_month = datetime.now().month
now_year = datetime.now().year
unix_day = int(datetime.strptime(f"{now_day}.{now_month}.{now_year} 0:0:0", "%d.%m.%Y %H:%M:%S").timestamp())
unix_day = int(
datetime.strptime(
f"{now_day}.{now_month}.{now_year} 0:0:0", "%d.%m.%Y %H:%M:%S"
).timestamp()
)
unix_week = unix_day - (now_week * 86400)
week_day = int(datetime.fromtimestamp(unix_week, pytz.timezone(BOT_TIMEZONE)).strftime("%d"))
week_month = int(datetime.fromtimestamp(unix_week, pytz.timezone(BOT_TIMEZONE)).strftime("%m"))
week_week = int(datetime.fromtimestamp(unix_week, pytz.timezone(BOT_TIMEZONE)).weekday())
week_day = int(
datetime.fromtimestamp(unix_week, pytz.timezone(BOT_TIMEZONE)).strftime("%d")
)
week_month = int(
datetime.fromtimestamp(unix_week, pytz.timezone(BOT_TIMEZONE)).strftime("%m")
)
week_week = int(
datetime.fromtimestamp(unix_week, pytz.timezone(BOT_TIMEZONE)).weekday()
)
return ded(f"""
<b>📊 СТАТИСТИКА БОТА</b>