diff --git a/README.md b/README.md
index 35404fa..ea807d1 100644
--- a/README.md
+++ b/README.md
@@ -27,31 +27,12 @@ YOOMONEY_CLIENT_ID=
Конфигурация читается только из `.env` или переменных окружения.
-5. Примени миграции:
-
-```bash
-python3 migrate.py up
-```
-
-6. Запусти бота:
+5. Запусти бота:
```bash
python3 main.py
```
-## Миграции
-
-Основные команды:
-
-```bash
-python3 migrate.py status
-python3 migrate.py up
-python3 migrate.py down
-python3 migrate.py auto "change description"
-```
-
-Схема БД теперь живет в `migrations/`, модели — в `tgbot/database/db_*.py`.
-
## Docker
```bash
diff --git a/migrate.py b/migrate.py
deleted file mode 100644
index fba8c8b..0000000
--- a/migrate.py
+++ /dev/null
@@ -1,188 +0,0 @@
-# - *- coding: utf- 8 - *-
-import argparse
-from typing import Optional
-
-from alembic import command
-from alembic.config import Config
-
-HELP_TEXT = """
-Миграции базы данных
-
-Безопасное правило:
- python migrate.py только показывает эту справку
- действия с БД выполняются только при явной команде
-
-Основные команды:
- python migrate.py help показать эту справку
- python migrate.py up применить все миграции до head
- python migrate.py down откатить последнюю миграцию
- python migrate.py new "add users" создать пустую миграцию
- python migrate.py auto "add users" создать миграцию по SQLAlchemy-моделям
- python migrate.py status показать текущую версию и последние версии
-
-Короткие алиасы:
- up -> upgrade
- down -> downgrade
- new -> revision
- auto -> revision --autogenerate
- autogen -> auto
- cur -> current
- hist -> history
- st -> status
-""".strip()
-
-
-# Сбор CLI-парсера миграций
-def build_parser() -> argparse.ArgumentParser:
- parser = argparse.ArgumentParser(
- description="Удобный CLI для Alembic-миграций",
- formatter_class=argparse.RawTextHelpFormatter,
- )
- subparsers = parser.add_subparsers(dest="command")
-
- help_parser = subparsers.add_parser("help", aliases=["h"], help="Показать понятную справку")
- help_parser.set_defaults(action=show_help)
-
- upgrade_parser = subparsers.add_parser("upgrade", aliases=["up"], help="Применить миграции")
- upgrade_parser.add_argument("revision", nargs="?", default="head", help="Версия миграции, по умолчанию head")
- upgrade_parser.set_defaults(action=run_upgrade)
-
- downgrade_parser = subparsers.add_parser("downgrade", aliases=["down"], help="Откатить миграции")
- downgrade_parser.add_argument("revision", nargs="?", default="-1", help="Версия отката, по умолчанию -1")
- downgrade_parser.set_defaults(action=run_downgrade)
-
- revision_parser = subparsers.add_parser("revision", aliases=["new"], help="Создать пустую миграцию")
- revision_parser.add_argument("message", nargs="?", help="Название миграции")
- revision_parser.add_argument("-m", "--message-option", dest="message_option", help="Название миграции")
- revision_parser.add_argument("--autogenerate", "-a", action="store_true", help="Собрать изменения из моделей")
- revision_parser.set_defaults(action=run_revision)
-
- auto_parser = subparsers.add_parser("auto", aliases=["autogen"], help="Создать миграцию по моделям")
- auto_parser.add_argument("message", nargs="?", help="Название миграции")
- auto_parser.add_argument("-m", "--message-option", dest="message_option", help="Название миграции")
- auto_parser.set_defaults(action=run_auto_revision)
-
- current_parser = subparsers.add_parser("current", aliases=["cur"], help="Показать текущую версию БД")
- current_parser.set_defaults(action=run_current)
-
- history_parser = subparsers.add_parser("history", aliases=["hist"], help="Показать историю миграций")
- history_parser.set_defaults(action=run_history)
-
- heads_parser = subparsers.add_parser("heads", help="Показать последние версии веток")
- heads_parser.set_defaults(action=run_heads)
-
- status_parser = subparsers.add_parser("status", aliases=["st"], help="Показать текущую версию и heads")
- status_parser.set_defaults(action=run_status)
-
- return parser
-
-
-# Просмотр справки по миграциям
-def show_help(_config: Optional[Config], _args: argparse.Namespace) -> None:
- print(HELP_TEXT)
-
-
-# Применение миграций до версии
-def run_upgrade(config: Config, args: argparse.Namespace) -> None:
- print(f"Применяю миграции до версии: {args.revision}")
- command.upgrade(config, args.revision)
- print("Готово")
-
-
-# Откат миграции до версии
-def run_downgrade(config: Config, args: argparse.Namespace) -> None:
- print(f"Откатываю миграции до версии: {args.revision}")
- command.downgrade(config, args.revision)
- print("Готово")
-
-
-# Создание пустой миграции
-def run_revision(config: Config, args: argparse.Namespace) -> None:
- message = get_revision_message(args)
- print(f"Создаю миграцию: {message}")
- config.attributes["autogenerate_empty"] = False
- command.revision(config, message=message, autogenerate=args.autogenerate)
- if config.attributes.get("autogenerate_empty"):
- print("Изменений в моделях не найдено, файл миграции не создан")
- return
-
- print("Готово")
-
-
-# Создание миграции по моделям
-def run_auto_revision(config: Config, args: argparse.Namespace) -> None:
- message = get_revision_message(args)
- print(f"Создаю миграцию по моделям: {message}")
- config.attributes["autogenerate_empty"] = False
- command.revision(config, message=message, autogenerate=True)
- if config.attributes.get("autogenerate_empty"):
- print("Изменений в моделях не найдено, файл миграции не создан")
- return
-
- print("Готово")
-
-
-# Просмотр текущей версии БД
-def run_current(config: Config, _args: argparse.Namespace) -> None:
- print("Текущая версия БД:")
- command.current(config)
-
-
-# Просмотр истории миграций
-def run_history(config: Config, _args: argparse.Namespace) -> None:
- print("История миграций:")
- command.history(config)
-
-
-# Просмотр heads миграций
-def run_heads(config: Config, _args: argparse.Namespace) -> None:
- print("Последние версии миграций:")
- command.heads(config)
-
-
-# Просмотр текущей версии и heads
-def run_status(config: Config, args: argparse.Namespace) -> None:
- run_current(config, args)
- print()
- run_heads(config, args)
-
-
-# Получение названия миграции
-def get_revision_message(args: argparse.Namespace) -> str:
- message: Optional[str] = args.message_option or args.message
-
- if not message:
- raise SystemExit("Укажи название миграции. Пример: python migrate.py auto \"add users\"")
-
- return message
-
-
-# Получение Alembic-конфига
-def get_config() -> Config:
- from tgbot.database.migration_runner import get_alembic_config
-
- return get_alembic_config()
-
-
-# Запуск CLI миграций
-def main() -> None:
- parser = build_parser()
- args = parser.parse_args()
-
- if args.command is None:
- show_help(None, args)
- return
-
- if args.action == show_help:
- show_help(None, args)
- return
-
- if args.action in (run_revision, run_auto_revision):
- get_revision_message(args)
-
- config = get_config()
- args.action(config, args)
-
-
-if __name__ == "__main__":
- main()
diff --git a/migrations/env.py b/migrations/env.py
deleted file mode 100644
index 385350e..0000000
--- a/migrations/env.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# - *- coding: utf- 8 - *-
-import asyncio
-from logging.config import fileConfig
-from typing import Any, List, Optional
-
-from alembic import context
-from alembic.operations import ops
-from sqlalchemy import pool, text
-from sqlalchemy.engine import Connection
-from sqlalchemy.schema import DefaultClause
-from sqlalchemy.ext.asyncio import async_engine_from_config
-
-from tgbot.database.core import Base
-
-import tgbot.database # noqa: F401
-
-config = context.config
-
-if config.config_file_name is not None:
- fileConfig(config.config_file_name)
-
-target_metadata = Base.metadata
-INDEX_DIFF_NOISE = {"ix_storage_payments_id", "ix_storage_settings_id"}
-
-
-# Отбрасывание SQLite-шума из автогенерации
-def _filter_autogenerated_ops(container: Any) -> None:
- filtered_ops = []
-
- for operation in container.ops:
- if isinstance(operation, ops.AlterColumnOp):
- continue
-
- if _is_index_noise(operation):
- continue
-
- if isinstance(operation, ops.ModifyTableOps):
- _filter_autogenerated_ops(operation)
-
- if operation.ops:
- filtered_ops.append(operation)
-
- continue
-
- filtered_ops.append(operation)
-
- container.ops = filtered_ops
-
-
-# Подготовка новых колонок к безопасному SQLite ADD COLUMN
-def _prepare_sqlite_add_columns(container: Any) -> None:
- for operation in container.ops:
- if isinstance(operation, ops.AddColumnOp):
- _apply_server_default(operation.column)
- continue
-
- if isinstance(operation, ops.ModifyTableOps):
- _prepare_sqlite_add_columns(operation)
-
-
-# Перенос default модели в server_default миграции
-def _apply_server_default(column: Any) -> None:
- if column.nullable or column.server_default is not None or column.default is None:
- return
-
- default_value = _get_simple_default(column.default.arg)
-
- if default_value is None:
- return
-
- column.server_default = DefaultClause(text(default_value))
-
-
-# Получение SQL-литерала для простых default-значений
-def _get_simple_default(value: Any) -> Optional[str]:
- if callable(value):
- return None
-
- if isinstance(value, bool):
- return "1" if value else "0"
-
- if isinstance(value, (int, float)):
- return str(value)
-
- if isinstance(value, str):
- return repr(value)
-
- return None
-
-
-# Игнорирование индексов, которые SQLite отражает иначе
-def _is_index_noise(operation: Any) -> bool:
- if not isinstance(operation, (ops.CreateIndexOp, ops.DropIndexOp)):
- return False
-
- return getattr(operation, "index_name", "") in INDEX_DIFF_NOISE
-
-
-# Ограничение сравнения SQLite только реальными добавлениями/удалениями
-def _include_object(object_: Any, name: str, type_: str, reflected: bool, compare_to: Any) -> bool:
- if type_ == "column" and compare_to is not None:
- return False
-
- if type_ == "index" and name in INDEX_DIFF_NOISE:
- return False
-
- return True
-
-
-# Очистка автосозданной миграции перед записью
-def _process_revision_directives(_context: Any, _revision: Any, directives: List[Any]) -> None:
- if not directives:
- return
-
- script = directives[0]
- _prepare_sqlite_add_columns(script.upgrade_ops)
- _filter_autogenerated_ops(script.upgrade_ops)
- _filter_autogenerated_ops(script.downgrade_ops)
-
- if not script.upgrade_ops.ops and not script.downgrade_ops.ops:
- config.attributes["autogenerate_empty"] = True
- directives.clear()
-
-
-# Запуск миграций без подключения
-def run_migrations_offline() -> None:
- url = config.get_main_option("sqlalchemy.url")
-
- context.configure(
- url=url,
- target_metadata=target_metadata,
- literal_binds=True,
- dialect_opts={"paramstyle": "named"},
- render_as_batch=True,
- compare_type=False,
- include_object=_include_object,
- process_revision_directives=_process_revision_directives,
- )
-
- with context.begin_transaction():
- context.run_migrations()
-
-
-# Запуск миграций на соединении
-def do_run_migrations(connection: Connection) -> None:
- context.configure(
- connection=connection,
- target_metadata=target_metadata,
- render_as_batch=True,
- compare_type=False,
- include_object=_include_object,
- process_revision_directives=_process_revision_directives,
- )
-
- with context.begin_transaction():
- context.run_migrations()
-
-
-# Запуск миграций через async-engine
-async def run_async_migrations() -> None:
- connectable = async_engine_from_config(
- config.get_section(config.config_ini_section, {}),
- poolclass=pool.NullPool,
- )
- connection = connectable.connect()
-
- try:
- await connection.start()
- await connection.run_sync(do_run_migrations)
- finally:
- await connection.close()
-
- await connectable.dispose()
-
-
-if context.is_offline_mode():
- run_migrations_offline()
-else:
- external_connection = config.attributes.get("connection")
-
- if external_connection is not None:
- do_run_migrations(external_connection)
- else:
- asyncio.run(run_async_migrations())
diff --git a/migrations/script.py.mako b/migrations/script.py.mako
deleted file mode 100644
index 590f5b3..0000000
--- a/migrations/script.py.mako
+++ /dev/null
@@ -1,24 +0,0 @@
-"""${message}
-
-Revision ID: ${up_revision}
-Revises: ${down_revision | comma,n}
-Create Date: ${create_date}
-"""
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-${imports if imports else ""}
-
-revision: str = ${repr(up_revision)}
-down_revision: Union[str, None] = ${repr(down_revision)}
-branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
-depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
-
-
-def upgrade() -> None:
- ${upgrades if upgrades else "pass"}
-
-
-def downgrade() -> None:
- ${downgrades if downgrades else "pass"}
diff --git a/migrations/versions/20260529_0001_shop_schema.py b/migrations/versions/20260529_0001_shop_schema.py
deleted file mode 100644
index 73bbb69..0000000
--- a/migrations/versions/20260529_0001_shop_schema.py
+++ /dev/null
@@ -1,515 +0,0 @@
-"""Начальная схема Телеграм-магазина
-
-Revision ID: 0001_shop_schema
-Revises:
-Create Date: 2026-05-29 00:00:00
-"""
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-
-revision: str = "0001_shop_schema"
-down_revision: Union[str, None] = None
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-# Проверка наличия таблицы
-def _table_exists(table_name: str) -> bool:
- bind = op.get_bind()
- result = bind.execute(
- sa.text(
- """
- SELECT name
- FROM sqlite_master
- WHERE type = 'table'
- AND name = :table_name
- """
- ),
- {"table_name": table_name},
- )
-
- return result.scalar_one_or_none() is not None
-
-
-# Проверка наличия колонки
-def _column_exists(table_name: str, column_name: str) -> bool:
- bind = op.get_bind()
- result = bind.execute(sa.text(f"PRAGMA table_info({table_name})"))
-
- return column_name in [row.name for row in result]
-
-
-# Добавление колонки, если ее нет
-def _add_column(table_name: str, column_name: str, column_sql: str) -> None:
- if not _column_exists(table_name, column_name):
- op.execute(sa.text(f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_sql}"))
-
-
-# Создание индекса, если его нет
-def _create_index(index_name: str, table_name: str, columns: str, *, unique: bool = False) -> None:
- unique_sql = "UNIQUE " if unique else ""
- op.execute(sa.text(f"CREATE {unique_sql}INDEX IF NOT EXISTS {index_name} ON {table_name} ({columns})"))
-
-
-# Нормализация текстовых bool-значений
-def _normalize_boolean_text(table_name: str, column_name: str, default_value: str) -> None:
- bind = op.get_bind()
- bind.execute(
- sa.text(
- f"""
- UPDATE {table_name}
- SET {column_name} = CASE LOWER(CAST(COALESCE({column_name}, :default_value) AS TEXT))
- WHEN '1' THEN 'True'
- WHEN 'true' THEN 'True'
- WHEN 'yes' THEN 'True'
- WHEN 'on' THEN 'True'
- WHEN 'да' THEN 'True'
- WHEN '0' THEN 'False'
- WHEN 'false' THEN 'False'
- WHEN 'no' THEN 'False'
- WHEN 'off' THEN 'False'
- WHEN 'нет' THEN 'False'
- ELSE :default_value
- END
- """
- ),
- {"default_value": default_value},
- )
-
-
-# Сбор SQL для переноса текстовой колонки
-def _copy_expr(table_name: str, column_name: str, default_value: str) -> str:
- if _column_exists(table_name, column_name):
- return f"COALESCE({column_name}, '{default_value}')"
-
- return f"'{default_value}'"
-
-
-# Сбор SQL для переноса числовой колонки
-def _copy_int_expr(table_name: str, column_name: str, default_value: int = 0) -> str:
- if _column_exists(table_name, column_name):
- return f"COALESCE({column_name}, {default_value})"
-
- return str(default_value)
-
-
-# Создание или правка таблицы пользователей
-def _ensure_users() -> None:
- bind = op.get_bind()
-
- if not _table_exists("storage_users"):
- op.create_table(
- "storage_users",
- sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
- sa.Column("user_id", sa.BigInteger(), nullable=False),
- sa.Column("user_login", sa.String(length=255), nullable=False, server_default=""),
- sa.Column("user_name", sa.String(length=255), nullable=False, server_default=""),
- sa.Column("user_surname", sa.String(length=255), nullable=False, server_default=""),
- sa.Column("user_fullname", sa.String(length=511), nullable=False, server_default=""),
- sa.Column("user_balance", sa.Float(), nullable=False, server_default="0"),
- sa.Column("user_refill", sa.Float(), nullable=False, server_default="0"),
- sa.Column("user_give", sa.Float(), nullable=False, server_default="0"),
- sa.Column("user_unix", sa.Integer(), nullable=False),
- sa.PrimaryKeyConstraint("increment"),
- )
- else:
- _add_column("storage_users", "user_login", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_users", "user_name", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_users", "user_surname", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_users", "user_fullname", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_users", "user_balance", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_users", "user_refill", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_users", "user_give", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_users", "user_unix", "INTEGER NOT NULL DEFAULT 0")
- bind.execute(sa.text("UPDATE storage_users SET user_login = COALESCE(user_login, '')"))
- bind.execute(sa.text("UPDATE storage_users SET user_name = COALESCE(user_name, '')"))
- bind.execute(sa.text("UPDATE storage_users SET user_surname = COALESCE(user_surname, '')"))
- bind.execute(
- sa.text(
- """
- UPDATE storage_users
- SET user_fullname = CASE
- WHEN user_fullname IS NULL OR user_fullname = '' THEN TRIM(user_name || ' ' || user_surname)
- ELSE user_fullname
- END
- """
- )
- )
- bind.execute(sa.text("UPDATE storage_users SET user_balance = COALESCE(user_balance, 0)"))
- bind.execute(sa.text("UPDATE storage_users SET user_refill = COALESCE(user_refill, 0)"))
- bind.execute(sa.text("UPDATE storage_users SET user_give = COALESCE(user_give, 0)"))
- bind.execute(sa.text("UPDATE storage_users SET user_unix = COALESCE(user_unix, 0)"))
- bind.execute(sa.text("DELETE FROM storage_users WHERE user_id IS NULL"))
- bind.execute(
- sa.text(
- """
- DELETE
- FROM storage_users
- WHERE user_id IS NOT NULL
- AND rowid NOT IN (
- SELECT MAX(rowid)
- FROM storage_users
- WHERE user_id IS NOT NULL
- GROUP BY user_id
- )
- """
- )
- )
-
- _create_index("ix_storage_users_user_id", "storage_users", "user_id", unique=True)
-
-
-# Создание или правка таблицы настроек
-def _ensure_settings() -> None:
- bind = op.get_bind()
-
- if not _table_exists("storage_settings"):
- op.create_table(
- "storage_settings",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("status_work", sa.String(length=16), nullable=False, server_default="True"),
- sa.Column("status_refill", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("status_buy", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("notification_refill", sa.String(length=16), nullable=False, server_default="True"),
- sa.Column("notification_buy", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("misc_faq", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_support", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_bot", sa.String(length=255), nullable=False, server_default="None"),
- sa.Column("misc_hosting_text", sa.String(length=64), nullable=False, server_default="telegraph"),
- sa.Column("misc_token_telegraph", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_discord_webhook_url", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_discord_webhook_name", sa.String(length=255), nullable=False, server_default="None"),
- sa.Column("misc_hide_category", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("misc_hide_position", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("misc_profit_day", sa.Integer(), nullable=False, server_default="0"),
- sa.Column("misc_profit_week", sa.Integer(), nullable=False, server_default="0"),
- sa.Column("misc_profit_month", sa.Integer(), nullable=False, server_default="0"),
- sa.PrimaryKeyConstraint("id"),
- )
- else:
- bind.execute(sa.text("DROP TABLE IF EXISTS storage_settings_new"))
- op.create_table(
- "storage_settings_new",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("status_work", sa.String(length=16), nullable=False, server_default="True"),
- sa.Column("status_refill", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("status_buy", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("notification_refill", sa.String(length=16), nullable=False, server_default="True"),
- sa.Column("notification_buy", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("misc_faq", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_support", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_bot", sa.String(length=255), nullable=False, server_default="None"),
- sa.Column("misc_hosting_text", sa.String(length=64), nullable=False, server_default="telegraph"),
- sa.Column("misc_token_telegraph", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_discord_webhook_url", sa.String(), nullable=False, server_default="None"),
- sa.Column("misc_discord_webhook_name", sa.String(length=255), nullable=False, server_default="None"),
- sa.Column("misc_hide_category", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("misc_hide_position", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("misc_profit_day", sa.Integer(), nullable=False, server_default="0"),
- sa.Column("misc_profit_week", sa.Integer(), nullable=False, server_default="0"),
- sa.Column("misc_profit_month", sa.Integer(), nullable=False, server_default="0"),
- sa.PrimaryKeyConstraint("id"),
- )
- bind.execute(
- sa.text(
- f"""
- INSERT INTO storage_settings_new (
- id,
- status_work,
- status_refill,
- status_buy,
- notification_refill,
- notification_buy,
- misc_faq,
- misc_support,
- misc_bot,
- misc_hosting_text,
- misc_token_telegraph,
- misc_discord_webhook_url,
- misc_discord_webhook_name,
- misc_hide_category,
- misc_hide_position,
- misc_profit_day,
- misc_profit_week,
- misc_profit_month
- )
- SELECT
- 1,
- {_copy_expr("storage_settings", "status_work", "True")},
- {_copy_expr("storage_settings", "status_refill", "False")},
- {_copy_expr("storage_settings", "status_buy", "False")},
- {_copy_expr("storage_settings", "notification_refill", "True")},
- {_copy_expr("storage_settings", "notification_buy", "False")},
- {_copy_expr("storage_settings", "misc_faq", "None")},
- {_copy_expr("storage_settings", "misc_support", "None")},
- {_copy_expr("storage_settings", "misc_bot", "None")},
- {_copy_expr("storage_settings", "misc_hosting_text", "telegraph")},
- {_copy_expr("storage_settings", "misc_token_telegraph", "None")},
- {_copy_expr("storage_settings", "misc_discord_webhook_url", "None")},
- {_copy_expr("storage_settings", "misc_discord_webhook_name", "None")},
- {_copy_expr("storage_settings", "misc_hide_category", "False")},
- {_copy_expr("storage_settings", "misc_hide_position", "False")},
- {_copy_int_expr("storage_settings", "misc_profit_day")},
- {_copy_int_expr("storage_settings", "misc_profit_week")},
- {_copy_int_expr("storage_settings", "misc_profit_month")}
- FROM storage_settings
- LIMIT 1
- """
- )
- )
- bind.execute(sa.text("DROP TABLE storage_settings"))
- bind.execute(sa.text("ALTER TABLE storage_settings_new RENAME TO storage_settings"))
-
- bind.execute(
- sa.text(
- """
- INSERT INTO storage_settings (id)
- SELECT 1
- WHERE NOT EXISTS (SELECT 1 FROM storage_settings WHERE id = 1)
- """
- )
- )
- for column, default in (
- ("status_work", "True"),
- ("status_refill", "False"),
- ("status_buy", "False"),
- ("notification_refill", "True"),
- ("notification_buy", "False"),
- ("misc_hide_category", "False"),
- ("misc_hide_position", "False"),
- ):
- _normalize_boolean_text("storage_settings", column, default)
-
- _create_index("ix_storage_settings_id", "storage_settings", "id", unique=True)
-
-
-# Создание или правка таблицы платежей
-def _ensure_payments() -> None:
- bind = op.get_bind()
-
- if not _table_exists("storage_payments"):
- op.create_table(
- "storage_payments",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("cryptobot_token", sa.String(), nullable=False, server_default="None"),
- sa.Column("yoomoney_token", sa.String(), nullable=False, server_default="None"),
- sa.Column("status_cryptobot", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("status_yoomoney", sa.String(length=16), nullable=False, server_default="False"),
- sa.PrimaryKeyConstraint("id"),
- )
- else:
- bind.execute(sa.text("DROP TABLE IF EXISTS storage_payments_new"))
- op.create_table(
- "storage_payments_new",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("cryptobot_token", sa.String(), nullable=False, server_default="None"),
- sa.Column("yoomoney_token", sa.String(), nullable=False, server_default="None"),
- sa.Column("status_cryptobot", sa.String(length=16), nullable=False, server_default="False"),
- sa.Column("status_yoomoney", sa.String(length=16), nullable=False, server_default="False"),
- sa.PrimaryKeyConstraint("id"),
- )
- bind.execute(
- sa.text(
- f"""
- INSERT INTO storage_payments_new (
- id,
- cryptobot_token,
- yoomoney_token,
- status_cryptobot,
- status_yoomoney
- )
- SELECT
- 1,
- {_copy_expr("storage_payments", "cryptobot_token", "None")},
- {_copy_expr("storage_payments", "yoomoney_token", "None")},
- {_copy_expr("storage_payments", "status_cryptobot", "False")},
- {_copy_expr("storage_payments", "status_yoomoney", "False")}
- FROM storage_payments
- LIMIT 1
- """
- )
- )
- bind.execute(sa.text("DROP TABLE storage_payments"))
- bind.execute(sa.text("ALTER TABLE storage_payments_new RENAME TO storage_payments"))
-
- bind.execute(
- sa.text(
- """
- INSERT INTO storage_payments (id)
- SELECT 1
- WHERE NOT EXISTS (SELECT 1 FROM storage_payments WHERE id = 1)
- """
- )
- )
- _normalize_boolean_text("storage_payments", "status_cryptobot", "False")
- _normalize_boolean_text("storage_payments", "status_yoomoney", "False")
- _create_index("ix_storage_payments_id", "storage_payments", "id", unique=True)
-
-
-# Создание или правка таблицы категорий
-def _ensure_category() -> None:
- if not _table_exists("storage_category"):
- op.create_table(
- "storage_category",
- sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
- sa.Column("category_id", sa.BigInteger(), nullable=False),
- sa.Column("category_name", sa.String(length=255), nullable=False),
- sa.Column("category_unix", sa.Integer(), nullable=False),
- sa.PrimaryKeyConstraint("increment"),
- )
- else:
- _add_column("storage_category", "category_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_category", "category_name", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_category", "category_unix", "INTEGER NOT NULL DEFAULT 0")
-
- _create_index("ix_storage_category_category_id", "storage_category", "category_id")
-
-
-# Создание или правка таблицы позиций
-def _ensure_position() -> None:
- if not _table_exists("storage_position"):
- op.create_table(
- "storage_position",
- sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
- sa.Column("category_id", sa.BigInteger(), nullable=False),
- sa.Column("position_id", sa.BigInteger(), nullable=False),
- sa.Column("position_name", sa.String(length=255), nullable=False),
- sa.Column("position_price", sa.Float(), nullable=False, server_default="0"),
- sa.Column("position_desc", sa.Text(), nullable=False, server_default="None"),
- sa.Column("position_photo", sa.Text(), nullable=False, server_default="None"),
- sa.Column("position_unix", sa.Integer(), nullable=False),
- sa.PrimaryKeyConstraint("increment"),
- )
- else:
- _add_column("storage_position", "category_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_position", "position_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_position", "position_name", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_position", "position_price", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_position", "position_desc", "TEXT NOT NULL DEFAULT 'None'")
- _add_column("storage_position", "position_photo", "TEXT NOT NULL DEFAULT 'None'")
- _add_column("storage_position", "position_unix", "INTEGER NOT NULL DEFAULT 0")
-
- _create_index("ix_storage_position_category_id", "storage_position", "category_id")
- _create_index("ix_storage_position_position_id", "storage_position", "position_id")
-
-
-# Создание или правка таблицы товаров
-def _ensure_item() -> None:
- if not _table_exists("storage_item"):
- op.create_table(
- "storage_item",
- sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
- sa.Column("user_id", sa.BigInteger(), nullable=False),
- sa.Column("category_id", sa.BigInteger(), nullable=False),
- sa.Column("position_id", sa.BigInteger(), nullable=False),
- sa.Column("item_id", sa.BigInteger(), nullable=False),
- sa.Column("item_unix", sa.Integer(), nullable=False),
- sa.Column("item_data", sa.Text(), nullable=False),
- sa.PrimaryKeyConstraint("increment"),
- )
- else:
- _add_column("storage_item", "user_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_item", "category_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_item", "position_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_item", "item_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_item", "item_unix", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_item", "item_data", "TEXT NOT NULL DEFAULT ''")
-
- _create_index("ix_storage_item_user_id", "storage_item", "user_id")
- _create_index("ix_storage_item_category_id", "storage_item", "category_id")
- _create_index("ix_storage_item_position_id", "storage_item", "position_id")
- _create_index("ix_storage_item_item_id", "storage_item", "item_id")
-
-
-# Создание или правка таблицы покупок
-def _ensure_purchases() -> None:
- if not _table_exists("storage_purchases"):
- op.create_table(
- "storage_purchases",
- sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
- sa.Column("user_id", sa.BigInteger(), nullable=False),
- sa.Column("user_balance_before", sa.Float(), nullable=False, server_default="0"),
- sa.Column("user_balance_after", sa.Float(), nullable=False, server_default="0"),
- sa.Column("purchase_receipt", sa.BigInteger(), nullable=False),
- sa.Column("purchase_data", sa.Text(), nullable=False),
- sa.Column("purchase_count", sa.Integer(), nullable=False, server_default="1"),
- sa.Column("purchase_price", sa.Float(), nullable=False, server_default="0"),
- sa.Column("purchase_price_one", sa.Float(), nullable=False, server_default="0"),
- sa.Column("purchase_position_id", sa.BigInteger(), nullable=False),
- sa.Column("purchase_position_name", sa.String(length=255), nullable=False),
- sa.Column("purchase_category_id", sa.BigInteger(), nullable=False),
- sa.Column("purchase_category_name", sa.String(length=255), nullable=False),
- sa.Column("purchase_unix", sa.Integer(), nullable=False),
- sa.PrimaryKeyConstraint("increment"),
- )
- else:
- _add_column("storage_purchases", "user_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "user_balance_before", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "user_balance_after", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "purchase_receipt", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "purchase_data", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_purchases", "purchase_count", "INTEGER NOT NULL DEFAULT 1")
- _add_column("storage_purchases", "purchase_price", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "purchase_price_one", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "purchase_position_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "purchase_position_name", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_purchases", "purchase_category_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_purchases", "purchase_category_name", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_purchases", "purchase_unix", "INTEGER NOT NULL DEFAULT 0")
-
- _create_index("ix_storage_purchases_user_id", "storage_purchases", "user_id")
- _create_index("ix_storage_purchases_purchase_receipt", "storage_purchases", "purchase_receipt")
- _create_index("ix_storage_purchases_purchase_position_id", "storage_purchases", "purchase_position_id")
- _create_index("ix_storage_purchases_purchase_category_id", "storage_purchases", "purchase_category_id")
-
-
-# Создание или правка таблицы пополнений
-def _ensure_refill() -> None:
- if not _table_exists("storage_refill"):
- op.create_table(
- "storage_refill",
- sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
- sa.Column("user_id", sa.BigInteger(), nullable=False),
- sa.Column("refill_receipt", sa.BigInteger(), nullable=False),
- sa.Column("refill_comment", sa.String(length=255), nullable=False, server_default=""),
- sa.Column("refill_amount", sa.Float(), nullable=False, server_default="0"),
- sa.Column("refill_method", sa.String(length=64), nullable=False),
- sa.Column("refill_unix", sa.Integer(), nullable=False),
- sa.PrimaryKeyConstraint("increment"),
- )
- else:
- _add_column("storage_refill", "user_id", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_refill", "refill_receipt", "INTEGER NOT NULL DEFAULT 0")
- _add_column("storage_refill", "refill_comment", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_refill", "refill_amount", "REAL NOT NULL DEFAULT 0")
- _add_column("storage_refill", "refill_method", "TEXT NOT NULL DEFAULT ''")
- _add_column("storage_refill", "refill_unix", "INTEGER NOT NULL DEFAULT 0")
-
- _create_index("ix_storage_refill_user_id", "storage_refill", "user_id")
- _create_index("ix_storage_refill_refill_receipt", "storage_refill", "refill_receipt")
-
-
-# Применение начальной схемы магазина
-def upgrade() -> None:
- _ensure_users()
- _ensure_settings()
- _ensure_payments()
- _ensure_category()
- _ensure_position()
- _ensure_item()
- _ensure_purchases()
- _ensure_refill()
-
-
-# Откат начальной схемы магазина
-def downgrade() -> None:
- op.drop_table("storage_refill")
- op.drop_table("storage_purchases")
- op.drop_table("storage_item")
- op.drop_table("storage_position")
- op.drop_table("storage_category")
- op.drop_table("storage_payments")
- op.drop_table("storage_settings")
- op.drop_table("storage_users")
diff --git a/migrations/versions/20260530_1915_492769059249_add_column_status_stars_into_storage_.py b/migrations/versions/20260530_1915_492769059249_add_column_status_stars_into_storage_.py
deleted file mode 100644
index 5e543cc..0000000
--- a/migrations/versions/20260530_1915_492769059249_add_column_status_stars_into_storage_.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""add column 'status_stars' into storage_payments
-
-Revision ID: 492769059249
-Revises: 0001_shop_schema
-Create Date: 2026-05-30 19:15:00.214301+03:00
-"""
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-
-
-revision: str = '492769059249'
-down_revision: Union[str, None] = '0001_shop_schema'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('storage_payments', schema=None) as batch_op:
- batch_op.add_column(sa.Column('status_stars', sa.String(length=16), server_default=sa.text("'False'"), nullable=False))
-
- # ### end Alembic commands ###
-
-
-def downgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('storage_payments', schema=None) as batch_op:
- batch_op.drop_column('status_stars')
-
- # ### end Alembic commands ###
diff --git a/migrations/versions/20260530_1928_700275128e27_add_column_stars_course_into_storage_.py b/migrations/versions/20260530_1928_700275128e27_add_column_stars_course_into_storage_.py
deleted file mode 100644
index ea3f605..0000000
--- a/migrations/versions/20260530_1928_700275128e27_add_column_stars_course_into_storage_.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""add column 'stars_course' into storage_payment
-
-Revision ID: 700275128e27
-Revises: 492769059249
-Create Date: 2026-05-30 19:28:42.490458+03:00
-"""
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-
-
-revision: str = '700275128e27'
-down_revision: Union[str, None] = '492769059249'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('storage_payments', schema=None) as batch_op:
- batch_op.add_column(sa.Column('stars_course', sa.Float(), server_default=sa.text('(1.5)'), nullable=False))
-
- # ### end Alembic commands ###
-
-
-def downgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('storage_payments', schema=None) as batch_op:
- batch_op.drop_column('stars_course')
-
- # ### end Alembic commands ###
diff --git a/migrations/versions/20260601_1934_36380377b0e6_add_column_misc_method_prod_into_.py b/migrations/versions/20260601_1934_36380377b0e6_add_column_misc_method_prod_into_.py
deleted file mode 100644
index 5d36be1..0000000
--- a/migrations/versions/20260601_1934_36380377b0e6_add_column_misc_method_prod_into_.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""add column 'misc_method_prod' into 'storage_settings'
-
-Revision ID: 36380377b0e6
-Revises: 700275128e27
-Create Date: 2026-06-01 19:34:35.858117+03:00
-"""
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-
-
-revision: str = '36380377b0e6'
-down_revision: Union[str, None] = '700275128e27'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('storage_settings', schema=None) as batch_op:
- batch_op.add_column(sa.Column('misc_method_prod', sa.String(length=16), server_default=sa.text("'skip'"), nullable=False))
-
- # ### end Alembic commands ###
-
-
-def downgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('storage_settings', schema=None) as batch_op:
- batch_op.drop_column('misc_method_prod')
-
- # ### end Alembic commands ###
diff --git a/pyproject.toml b/pyproject.toml
index 69c96fa..f67cd4c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -28,4 +28,3 @@ dependencies = [
[tool.setuptools.packages.find]
include = ["tgbot*"]
-exclude = ["migrations*"]
diff --git a/tgbot/data/config.py b/tgbot/data/config.py
index c8ec410..31bf2a0 100644
--- a/tgbot/data/config.py
+++ b/tgbot/data/config.py
@@ -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
diff --git a/tgbot/database/db_position.py b/tgbot/database/db_position.py
index ef71886..63adac1 100644
--- a/tgbot/database/db_position.py
+++ b/tgbot/database/db_position.py
@@ -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()
+ }
diff --git a/tgbot/database/db_settings.py b/tgbot/database/db_settings.py
index 826539b..3ac9810 100644
--- a/tgbot/database/db_settings.py
+++ b/tgbot/database/db_settings.py
@@ -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)
diff --git a/tgbot/database/entities.py b/tgbot/database/entities.py
index 3033f7c..c9b97c4 100644
--- a/tgbot/database/entities.py
+++ b/tgbot/database/entities.py
@@ -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
diff --git a/tgbot/database/migration_runner.py b/tgbot/database/migration_runner.py
index abb02fd..3d08120 100644
--- a/tgbot/database/migration_runner.py
+++ b/tgbot/database/migration_runner.py
@@ -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
diff --git a/tgbot/database/repository.py b/tgbot/database/repository.py
index 3256ef2..38f119d 100644
--- a/tgbot/database/repository.py
+++ b/tgbot/database/repository.py
@@ -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
diff --git a/tgbot/keyboards/inline_admin.py b/tgbot/keyboards/inline_admin.py
index 0b54e95..f509bb1 100644
--- a/tgbot/keyboards/inline_admin.py
+++ b/tgbot/keyboards/inline_admin.py
@@ -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()
diff --git a/tgbot/keyboards/inline_admin_products.py b/tgbot/keyboards/inline_admin_products.py
index 2901401..b62598e 100644
--- a/tgbot/keyboards/inline_admin_products.py
+++ b/tgbot/keyboards/inline_admin_products.py
@@ -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()
diff --git a/tgbot/routers/admin/admin_products.py b/tgbot/routers/admin/admin_products.py
index 3ad280f..d73921e 100644
--- a/tgbot/routers/admin/admin_products.py
+++ b/tgbot/routers/admin/admin_products.py
@@ -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(
"❌ Название не может превышать 50 символов\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(
"❌ Название не может превышать 50 символов\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(
- "❌ Данные были введены неверно\n"
- "📁 Введите цену для позиции",
+ "❌ Данные были введены неверно\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(
- "📁 Отправьте новое изображение для позиции\n"
- "❕ Отправьте 0 чтобы пропустить.",
- 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(
- "🧿 Отсутствует Дискорд вебхук, добавьте его в настройках"
- )
-
- position_photo = "None"
-
- if message.photo is not None:
- cache_message = await message.answer(
- "♻️ Подождите, фотография загружается..."
- )
-
- 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(
"📁 Выберите позицию для изменения 🖍",
- 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,
🎁 Отправляйте данные товаров
❗ Товары разделяются одной пустой строчкой. Пример:
Данные товара...
-
+
Данные товара...
-
+
Данные товара...
"""),
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("⌛ Ждите, товары добавляются..")
@@ -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"""
✅ Вы успешно удалили все категории
🗃 Категорий: {get_categories}шт
📁 Позиций: {get_positions}шт
🎁 Товаров: {get_items}шт
- """)
- )
+ """))
# Удаление всех позиций
@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"""
✅ Вы успешно удалили все позиции
📁 Позиций: {get_positions}шт
🎁 Товаров: {get_items}шт
- """)
- )
+ """))
# Удаление всех товаров
diff --git a/tgbot/routers/admin/admin_settings.py b/tgbot/routers/admin/admin_settings.py
index b8ec83d..4f88298 100644
--- a/tgbot/routers/admin/admin_settings.py
+++ b/tgbot/routers/admin/admin_settings.py
@@ -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("""
❔ Введите новый текст для FAQ
❕ Вы можете использовать заготовленный синтаксис и HTML разметку:
▪️ {username} - логин пользоваля
▪️ {user_id} - айди пользователя
▪️ {firstname} - имя пользователя
- """)
- )
+ """))
# Изменение поддержки
@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"""
- 🧿 Отправьте новый вебхук дискорда
- ❕ Для удаления вебхука введите 0
- ❕ Вы можете использовать публичный вебхук, но ответственность за его использование лежит только на вас
- ▪️ Публичный вебхук: {get_discord_public_webhook}
- """)
- )
-
-
# Изменение текстового хостинга по умолчанию
@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(
- "❌ Ошибка синтаксиса HTML\n"
- "❔ Введите новый текст для FAQ",
+ "❌ Ошибка синтаксиса HTML\n" "❔ Введите новый текст для FAQ",
)
await state.clear()
@@ -310,67 +303,3 @@ async def settings_support_get(message: Message, bot: Bot, state: FSM, arSession
"🖍 Изменение данных бота",
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(
- "⚙️ Настройки бота",
- reply_markup=await settings_finl(),
- )
-
- # Добавление нового вебхука
- cache_message = await message.answer("♻️ Проверка дискорд вебхука..")
-
- 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(
- "⚙️ Настройки бота",
- reply_markup=await settings_finl(),
- )
-
- # Обработка ошибки добавления вебхука
- get_discord_public_webhook = await (
- DiscordDJ(
- arSession=arSession,
- bot=bot,
- )
- ).export_webhook()
-
- await cache_message.edit_text(
- ded(f"""
- ❌ Указан некорректный вебхук
- ➖➖➖➖➖➖➖➖➖➖
- 🧿 Отправьте новый вебхук дискорда
- ❕ Для удаления вебхука введите 0
- ❕ Вы можете использовать публичный вебхук, но ответственность за его использование лежит только на вас
- ▪️ Публичный вебхук: {get_discord_public_webhook}
- """)
- )
diff --git a/tgbot/routers/user/user_menu.py b/tgbot/routers/user/user_menu.py
index 8060d9f..04016de 100644
--- a/tgbot/routers/user/user_menu.py
+++ b/tgbot/routers/user/user_menu.py
@@ -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)
diff --git a/tgbot/services/api_discord.py b/tgbot/services/api_discord.py
deleted file mode 100644
index e2a4408..0000000
--- a/tgbot/services/api_discord.py
+++ /dev/null
@@ -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"🧿 Дискорд вебхук недоступен. Как можно быстрее его замените\n"
- f"❗️ Ошибка: {error_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']
diff --git a/tgbot/utils/const_functions.py b/tgbot/utils/const_functions.py
index 34e468b..7795ea4 100644
--- a/tgbot/utils/const_functions.py
+++ b/tgbot/utils/const_functions.py
@@ -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")
diff --git a/tgbot/utils/misc_functions.py b/tgbot/utils/misc_functions.py
index 3fc7b2b..017dd4f 100644
--- a/tgbot/utils/misc_functions.py
+++ b/tgbot/utils/misc_functions.py
@@ -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"""
✅ Бот был успешно запущен
➖➖➖➖➖➖➖➖➖➖
- {get_text_desc()}
- ➖➖➖➖➖➖➖➖➖➖
❗ Данное сообщение видят только администраторы бота.
"""),
)
@@ -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"📢 Рассылка началась... ({users_count}/{len(get_users)})")
+ await call.message.edit_text(
+ f"📢 Рассылка началась... ({users_count}/{len(get_users)})"
+ )
await asyncio.sleep(0.07)
- await call.message.edit_text(
- ded(f"""
+ await call.message.edit_text(ded(f"""
📢 Рассылка была завершена за {get_unix() - get_time}сек
➖➖➖➖➖➖➖➖➖➖
👤 Всего пользователей: {len(get_users)}
✅ Пользователей получило сообщение: {users_receive}
❌ Пользователей не получило сообщение: {users_block}
- """)
- )
+ """))
diff --git a/tgbot/utils/text_functions.py b/tgbot/utils/text_functions.py
index 4634da4..7301eae 100644
--- a/tgbot/utils/text_functions.py
+++ b/tgbot/utils/text_functions.py
@@ -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"""
- 🎁 Покупка товара{hide_link(get_position.position_photo)}
+ 🎁 Покупка товара
➖➖➖➖➖➖➖➖➖➖
▪️ Название: {get_position.position_name}
▪️ Категория: {get_category.category_name}
@@ -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 = "Присутствует ✅"
- else:
- position_photo_text = "Отсутствует ❌"
-
# Наличие описания
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"""
- 📁 Редактирование позиции{hide_link(get_position.position_photo)}
+ 📁 Редактирование позиции
➖➖➖➖➖➖➖➖➖➖
▪️ Категория: {get_category.category_name}
▪️ Позиция: {get_position.position_name}
▪️ Стоимость: {get_position.position_price}₽
▪️ Количество: {len(get_items)}шт
▪️ Дата создания: {convert_date(get_category.category_unix)}
- ▪️ Изображение: {position_photo_text}
▪️ Описание: {position_desc}
💸 Продаж за День: {profit_count_day}шт - {round(profit_amount_day, 2)}₽
@@ -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"""
📊 СТАТИСТИКА БОТА