Разработка своего собственного решения для автопродаж на python

Undestiny

Профи разработки tg Undestiny
Программист
Подтвержденный
Сообщения
254
Реакции
1.164
Продажи
14
Кешбек
24.58$
Здравствуйте. Редко участвую в конкурсах, однако вот.

Решил расписать вам технологию изготовления мной, специального решения автопродаж. Написано с нуля, и решает все основные вопросы, которые нужны для подобного типа решений.Это будут бот(ы) для телеграм и удобная веб админка для управления всем функционалом.
Присутствует защита от блокировок. А также рассылка от ботов, и от юзеров,чтобы сохранять клиентов всегда. Оплата принимается ботов на автомате с помощью usdt trc-20.

И другие плюшки!


Глава 1. Инструментарий.

В качестве основного языка взят, идеальный на мой взгляд язык – python. Он сочетает в себе простоту синтаксиса, мощь открытых библиотек и скорость разработки. На самом деле на нем очень хорошо писать веб решения так, как он интерпретируем и имеет множество идеальных заготовок.

Библиотеки flask sqlalchemy, jinja2, pyTelegramBotAPI, telethon.

Основные файлы:​

  • app.py - Flask веб-приложение с админ-панелью
  • bot_handlers.py - Обработчики Telegram-бота
  • models.py - Модели базы данных SQLAlchemy
  • run.py - Точка входа для запуска приложения
  • tron_payment_checker.py - Проверка USDT платежей
  • user_broadcast.py - Система массовых рассылок

Конфигурация:​

  • requirements.txt - Зависимости Python
  • .env.example - Пример файла окружения

Глава 2. Программируем.

В качестве основного языка разработки был использован python, и его микрофреймворк flask. Внутри также разработка на шаблонах jinja2 (тот же html,только с удобными тегами автоматизации) и далее css,js для красивостей.
Для базы данных стоит по умолчанию sqlite, но так как используется система sqlalchemy, то можно просто в конфиге поменять и на более мощные при желании базы mysql,postgresql.

Сначала нужно инициализировать проект.

# Создание виртуального окружения
Код:
python3 -m venv .
source bin/activate  # Linux/Mac
# или
# Scripts\activate  # Windows

# Установка зависимостей
pip install Flask==2.3.3 Flask-SQLAlchemy==3.0.5 Flask-Migrate==4.0.5 pyTelegramBotAPI==4.14.0 python-dotenv==1.0.0 qrcode==7.4.2 Pillow==10.0.1 Telethon==1.34.0

Создать файл конфиг
Код:
# Секретный ключ для Flask

SECRET_KEY=your-very-secret-key-here-change-this-in-production
# URL базы данных

DATABASE_URL=sqlite:///bot.db

# Режим Flask

FLASK_ENV=development

# Порт и хост

FLASK_PORT=5000
FLASK_HOST=0.0.0.0

Создать модели базы данных. Написано коротко, не все написал чтобы не засорять статью. Пример юзер модели добавил.

Код:
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import json

# Создаем объект db
db = SQLAlchemy()

class User(db.Model):

"""Модель для хранения информации о пользователях"""

__tablename__ = 'users'

id = db.Column(db.Integer, primary_key=True)
telegram_id = db.Column(db.BigInteger, nullable=False)
bot_id = db.Column(db.Integer, db.ForeignKey('bots.id'), nullable=False)
username = db.Column(db.String(100), nullable=True)
first_name = db.Column(db.String(100), nullable=True)
last_name = db.Column(db.String(100), nullable=True)
phone_number = db.Column(db.String(20), nullable=True)
language_code = db.Column(db.String(10), nullable=True)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_activity = db.Column(db.DateTime, default=datetime.utcnow)

# Уникальность по telegram_id и bot_id

__table_args__ = (db.UniqueConstraint('telegram_id', 'bot_id', name='unique_user_bot'),)

def __repr__(self):
return f'<User {self.telegram_id}>'
Создаем flask приложение app.py
Код:
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session
from flask_migrate import Migrate
from functools import wraps

import os
from datetime import datetime
import threading

import telebot

import json

import logging

import hashlib



# Настройка логирования

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)



app = Flask(__name__)

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'your-secret-key-here')

app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///bot.db')

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False



# Импорт db из models

from models import db, Bot, User, Setting, Address



# Инициализация db с приложением

db.init_app(app)

migrate = Migrate(app, db)



# Словарь активных ботов

active_bots = {}



# Функции аутентификации

def get_admin_password():

"""Получить пароль администратора из настроек"""

setting = Setting.query.filter_by(key='admin_password').first()

return setting.value if setting else ''



def hash_password(password):

"""Хеширование пароля"""

return hashlib.sha256(password.encode()).hexdigest()



def check_password(password):

"""Проверка пароля"""

stored_password = get_admin_password()

if not stored_password:

return password == ''

return hash_password(password) == stored_password



def login_required(f):

"""Декоратор для проверки авторизации"""

@wraps(f)

def decorated_function(*args, **kwargs):

if not session.get('authenticated'):

return redirect(url_for('login'))

return f(*args, **kwargs)

return decorated_function



@app.route('/login', methods=['GET', 'POST'])

def login():

"""Страница входа"""

if request.method == 'POST':

password = request.form.get('password', '')



if check_password(password):

session['authenticated'] = True

flash('Успешный вход в систему!', 'success')

return redirect(url_for('index'))

else:

flash('Неверный пароль!', 'error')



return render_template('login.html')



@app.route('/logout')

def logout():

"""Выход из системы"""

session.pop('authenticated', None)

flash('Вы вышли из системы', 'info')

return redirect(url_for('login'))



@app.route('/')

@login_required

def index():

"""Главная страница админ-панели"""

bots = Bot.query.all()

users_count = User.query.count()

active_users_count = User.query.filter_by(is_active=True).count()



return render_template('index.html',

bots=bots,

users_count=users_count,

active_users_count=active_users_count)



@app.route('/bots')

@login_required

def bots():

"""Страница управления ботами"""

bots = Bot.query.all()

return render_template('bots.html', bots=bots)



@app.route('/users')

@login_required

def users():

"""Страница управления пользователями"""

users = User.query.all()

return render_template('users.html', users=users)



if __name__ == '__main__':

app.run(debug=True)
Далее нужно создать файл запуска run.py и файлы шаблонов html jinja2 в папке templates для описания интерфейса админки.

Код:
#!/usr/bin/env python3



import os

from dotenv import load_dotenv



# Загружаем переменные окружения

load_dotenv()



from app import app, db



if __name__ == '__main__':

# Создаем таблицы базы данных

with app.app_context():

db.create_all()

print("База данных инициализирована")



# Получаем настройки

host = os.environ.get('FLASK_HOST', '0.0.0.0')

port = int(os.environ.get('FLASK_PORT', 5000))

debug = os.environ.get('FLASK_ENV', 'production') == 'development'



print(f"Запуск Bot на {host}:{port}")

print(f"Веб-админка: http://{host}:{port}")



# Запускаем приложение

app.run(host=host, port=port, debug=debug)
[CODE]

[CENTER][ATTACH type="full" width="828px" size="1199x499"]2271599[/ATTACH]
Основная страница админки

[/CENTER]

Каждый бот запускается в отдельном потоке,что удобно и безопасно, и позволяет управлять целой армией ботов.
[CODE]
def run_bot():

try:

telegram_bot.polling(none_stop=True)

except Exception as e:

logger.error(f"Ошибка в потоке бота {bot_id}: {e}")



bot_thread = threading.Thread(target=run_bot)

bot_thread.daemon = True

bot_thread.start()


Учтите при,что все операции с базой необходимо выполнять в контексте app.

with app.app_context():

user = get_or_create_user(message, bot_id)

# работа с базой данных

Для безопасности было реализовано,что админка доступна лишь с локалхоста, то есть с тор домена. И защищена паролем.
Также бот помнит на каком этапе находится каждый из пользователей благодаря системе сессий.

Также сам бот было решено создать многоязычным поэтому реализована система переводов внутри русский и грузинский, вместе с любыми параметрами. В коде существует словарь TEXTS с переводами на русский и грузинский языки, содержащий более 100 текстовых строк. Это не просто набор фраз — это продуманная система, где каждое сообщение имеет свой ключ и может содержать параметры для подстановки.

'order_created': '✅ Заказ #{order_id} создан!\n\n Сумма: ${amount} USDT\n Адрес для оплаты:\n\n`{address}`'







1756481362591.png


Рассылка через ботов также возможна​

Реализация обработчиков бота в телеграм.
Код:
from telebot import types

from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton

import json

import os

from datetime import datetime

from models import db, User, Bot, Setting

import logging



logger = logging.getLogger(__name__)



# Словари для локализации

TEXTS = {

'ru': {

'welcome': ' Добро пожаловать! Выберите язык:',

'main_menu': ' Главное меню',

'language_selected': '✅ Язык выбран: Русский',

'contact_operator': '‍ Связаться с оператором',

'catalog': '️ Каталог товаров',

'my_orders': ' Мои заказы',

'settings': '⚙️ Настройки',

'change_language': ' Изменить язык',

},

'en': {

'welcome': ' Welcome! Choose your language:',

'main_menu': ' Main menu',

'language_selected': '✅ Language selected: English',

'contact_operator': '‍ Contact operator',

'catalog': '️ Product catalog',

'my_orders': ' My orders',

'settings': '⚙️ Settings',

'change_language': ' Change language',

}

}



def get_text(user_lang, key):

"""Получить текст на языке пользователя"""

return TEXTS.get(user_lang, TEXTS['ru']).get(key, key)



def create_main_menu_keyboard(user_lang='ru'):

"""Создать главное меню"""

keyboard = InlineKeyboardMarkup(row_width=2)



buttons = [

InlineKeyboardButton(get_text(user_lang, 'catalog'), callback_data='catalog'),

InlineKeyboardButton(get_text(user_lang, 'my_orders'), callback_data='my_orders'),

InlineKeyboardButton(get_text(user_lang, 'contact_operator'), callback_data='contact_operator'),

InlineKeyboardButton(get_text(user_lang, 'settings'), callback_data='settings'),

]



keyboard.add(*buttons)

return keyboard



def create_language_keyboard():

"""Создать клавиатуру выбора языка"""

keyboard = InlineKeyboardMarkup(row_width=2)

keyboard.add(

InlineKeyboardButton(' Русский', callback_data='lang_ru'),

InlineKeyboardButton(' English', callback_data='lang_en')

)

return keyboard



def create_bot_handlers(bot, bot_model):

"""Создать обработчики для бота"""



@bot.message_handler(commands=['start'])

def handle_start(message):

"""Обработчик команды /start"""

try:

# Получаем или создаем пользователя

user = User.query.filter_by(

telegram_id=message.from_user.id,

bot_id=bot_model.id

).first()



if not user:

user = User(

telegram_id=message.from_user.id,

bot_id=bot_model.id,

username=message.from_user.username,

first_name=message.from_user.first_name,

last_name=message.from_user.last_name,

language_code=message.from_user.language_code or 'ru'

)

db.session.add(user)

db.session.commit()



# Обновляем активность

user.last_activity = datetime.utcnow()

db.session.commit()



# Отправляем приветствие

bot.send_message(

message.chat.id,

get_text(user.language_code, 'welcome'),

reply_markup=create_language_keyboard()

)



except Exception as e:

logger.error(f"Ошибка в handle_start: {e}")

bot.send_message(message.chat.id, "Произошла ошибка. Попробуйте позже.")



@bot.callback_query_handler(func=lambda call: call.data.startswith('lang_'))

def handle_language_selection(call):

"""Обработчик выбора языка"""

try:

lang = call.data.split('_')[1]



# Обновляем язык пользователя

user = User.query.filter_by(

telegram_id=call.from_user.id,

bot_id=bot_model.id

).first()



if user:

user.language_code = lang

db.session.commit()



# Отправляем главное меню

bot.edit_message_text(

get_text(lang, 'language_selected'),

call.message.chat.id,

call.message.message_id

)



bot.send_message(

call.message.chat.id,

get_text(lang, 'main_menu'),

reply_markup=create_main_menu_keyboard(lang)

)



except Exception as e:

logger.error(f"Ошибка в handle_language_selection: {e}")



@bot.callback_query_handler(func=lambda call: True)

def handle_callback_query(call):

"""Обработчик callback запросов"""

try:

user = User.query.filter_by(

telegram_id=call.from_user.id,

bot_id=bot_model.id

).first()



if not user:

return



user_lang = user.language_code or 'ru'



if call.data == 'catalog':

bot.answer_callback_query(call.id, "Каталог в разработке")

elif call.data == 'my_orders':

bot.answer_callback_query(call.id, "Заказы в разработке")

elif call.data == 'contact_operator':

bot.answer_callback_query(call.id, "Связь с оператором в разработке")

elif call.data == 'settings':

bot.edit_message_text(

get_text(user_lang, 'settings'),

call.message.chat.id,

call.message.message_id,

reply_markup=create_language_keyboard()

)



except Exception as e:

logger.error(f"Ошибка в handle_callback_query: {e}")



return bot


Ниже приложил пример реализации самого интерфейса бота в тг.

1756482008154.png


Глава 3. Тестируем, размышляем, дополняем.



Для защит от различных блокировок конкурентов, переборов и прочих нюансов было решено придумать удобные решения. Путем анализа других ботов и проверки своего создали некоторые моменты, которые решают большинство проблем.

А именно:
  1. Задержки между сообщениями:
Код:
	# Небольшая задержка между отправками
	await asyncio.sleep(1)
  • Ограничения в интерфейсе веб-админки
Код:
<li class="mb-2">

 
<i class="fas fa-check text-success"></i>

 
<small>Не отправляйте более 30 сообщений в минуту</small>

 
</li>


  • Обработка ошибок
Код:
try:

 
await client.send_message(entity, message)

 
sent_count += 1

 
except Exception as e:

 
failed_count += 1

 
errors.append(f"{target}: {str(e)}")

  • Система капчи для проверки при входе в бота юзера
Код:
def create_captcha_for_user(user_id, bot_id):

 
# Генерируем случайные эмодзи

 
all_emojis = ['', '', '', '⭐', '', '', '', '']

 
target_emoji = random.choice(all_emojis)

 

 
# Создаем варианты ответов

 
emoji_options = [target_emoji]

 
while len(emoji_options) < 6:

 
emoji = random.choice(all_emojis)

 
If emoji not in emoji_options:

 
emoji_options.append(emoji)

 

 
random.shuffle(emoji_options)


  • Возможность запуска множества ботов одновременно. В админке можно по нажатию клавиш и форм создать множество ботов и включать их в разных потоках независимо.

1756482036604.png


Код:
# Словарь активных ботов

 
active_bots = {}

 

 
@app.route('/start_bot/<int:bot_id>')

 
def start_bot(bot_id):

 
# Каждый бот запускается в отдельном потоке

 
bot_thread = threading.Thread(target=run_bot)

 
bot_thread.daemon = True

 
bot_thread.start()

 

 
active_bots[bot_id] = {

 
'bot': telegram_bot,

 
'thread': bot_thread

 
}




  • Самая мощная функция — рассылки через пользовательские аккаунты с помощью telethon. Это обходит многие ограничения обычного апи тг. Нужно для того,чтобы когда заблокируют одного бота можно было разослать старым пользователям через обычного юзера оповещение о новых ботах или каналах,форумах. Таким образом ваши клиенты всегда при вас. Отправа идет по username, или номеру если у пользователя нет ника.

Код:
class UserBroadcastManager:

 
async def send_broadcast(self, phone, message, targets, api_id, api_hash):

 
client = TelegramClient(session_file, api_id, api_hash)

 

 
for target in targets:

 
try:

 
# Определяем тип цели (username или номер)

 
if target.startswith('@'):

 
entity = await client.get_entity(target)

 
elif target.startswith('+') or target.isdigit():

 
entity = await client.get_entity(target)

 

 
await client.send_message(entity, message)

 
sent_count += 1

 

 
# Задержка между отправками

 
await asyncio.sleep(1)

 

 
except Exception as e:

 
failed_count += 1

  
Можно легко добавлять аккаунты пользователей(для рассылки) в админке и получать их сессии вписав код с тг.

 
function startAuth() {

 
// Двухэтапная авторизация

 
// 1. Отправка кода на телефон

 
// 2. Подтверждение кода (+ 2FA если нужно)

 

 
fetch('/user_broadcast/auth', {

 
method: 'POST',

 
body: formData

 
})

 
.then(response => response.json())

 
.then(data => {

 
if (data.success) {

 
// Переход ко второму шагу

 
showCodeInput();

 
}

 
});

 
}




  • Особенно элегантно решена задача с QR-кодами для платежей. Функция generate_qr_code() не только создает QR-код с настраиваемым дизайном (цвета, размер, логотип), но и schedule_qr_deletion() автоматически удаляет его через заданное время. Это повышает безопасность для ваших тормознутых клиентов.

  • Колесо фортуны. Функцию работающая через сайт и выдающая коды бонусы и скидки.


1756481965720.png






  • Одна из самых интересных функций get_smart_stash(). Это не просто выбор случайного товара со склада. Система анализирует доступные клады и выбирает оптимальный по нескольким критериям:

Приоритет по весу и расположению — сначала ищет точное совпадение
Временные факторы — предпочитает более старые клады

Это обеспечивает эффективную ротацию товаров и оптимизацию логистики. Очень удобно при автоматических продажах
Код:
def get_smart_stash(product_id, weight):

 
"""

 
Получить клад с умной логикой распределения.

 
Выбирает клады с разницей по загрузке минимум в 5 позиций.

 
Логика: 1-й, 6-й, 11-й, 16-й, потом 2-й, 7-й, 12-й, 3-й, 8-й и т.д.

 
"""

 
try:

 
# Получаем все доступные клады для данного продукта и веса, отсортированные по ID (порядок загрузки)

 
available_stashes = Stash.query.filter_by(

 
product_id=product_id,

 
weight=weight,

 
is_sold=False,

 
order_item_id=None

 
).order_by(Stash.id).all()

 

 
if not available_stashes:

 
return None

 

 
# Если кладов меньше 5, просто берем первый

 
if len(available_stashes) < 5:

 
return available_stashes[0]

 

 
# Получаем ключ для отслеживания текущего паттерна выбора

 
# Используем комбинацию product_id и weight как ключ

 
pattern_key = f"stash_pattern_{product_id}_{weight}"

 

 
# Получаем или создаем настройку для отслеживания паттерна

 
pattern_setting = Setting.query.filter_by(key=pattern_key).first()

 
if not pattern_setting:

 
pattern_setting = Setting(key=pattern_key, value="0,0")  # current_group, position_in_group

 
db.session.add(pattern_setting)

 
db.session.commit()

 

 
# Парсим текущее состояние паттерна

 
try:

 
current_group, position_in_group = map(int, pattern_setting.value.split(','))

 
except:

 
current_group, position_in_group = 0, 0

 

 
# Вычисляем индекс клада для выбора

 
# Группы: 0 = позиции 0,5,10,15... | 1 = позиции 1,6,11,16... | и т.д.

 
stash_index = current_group + (position_in_group * 5)

 

 
# Если индекс выходит за границы, переходим к следующей группе

 
if stash_index >= len(available_stashes):

 
current_group += 1

 
position_in_group = 0

 

 
# Если все группы пройдены, начинаем сначала

 
if current_group >= 5:

 
current_group = 0

 
position_in_group = 0

 

 
stash_index = current_group + (position_in_group * 5)

 

 
# Если все еще выходит за границы, берем первый доступный

 
if stash_index >= len(available_stashes):

 
stash_index = 0

 
current_group = 0

 
position_in_group = 0

 

 
# Выбираем клад

 
selected_stash = available_stashes[stash_index]

 

 
# Обновляем паттерн для следующего выбора

 
position_in_group += 1

 

 
# Если достигли конца текущей группы, переходим к следующей

 
max_positions_in_group = (len(available_stashes) - current_group + 4) // 5  # Округление вверх

 
if position_in_group >= max_positions_in_group:

 
current_group += 1

 
position_in_group = 0

 

 
# Если все группы пройдены, начинаем сначала

 
if current_group >= 5:

 
current_group = 0

 

 
# Сохраняем обновленный паттерн

 
pattern_setting.value = f"{current_group},{position_in_group}"

 
db.session.commit()

 

 
logger.info(f"Выбран клад ID={selected_stash.id} (индекс {stash_index} из {len(available_stashes)}) для продукта {product_id}, вес {weight}")

 

 
return selected_stash

 

 
except Exception as e:

 
logger.error(f"Ошибка в get_smart_stash: {str(e)}")

 
# В случае ошибки возвращаем первый доступный клад

 
return Stash.query.filter_by(

 
product_id=product_id,

 
weight=weight,

 
is_sold=False,

 
order_item_id=None

 
).first()

Послесловие:


Как вы видите нет ничего сложного в своем собственном решении для автопродаж.
При прямых руках и желании вы можете создать себе независимый от сторонних программных решений автоматизированный бизнес с ботами и админкой.

Если будут дополнительные вопросы по реализации подобных решений или конкретным интересным идеям, пишите в вопросы к статье, помогу чем смогу.
Спасибо за внимание!!! Процветания вашим проектам и бизнесам!​
 

Вложения

  • 1756481285339.png
    1756481285339.png
    100.9 КБ · Просмотры: 74
Скрипты тоооочно не написанны нейронкой)
 
Все не чего, только статьи на конкурс принимались до 29 августа, могут не зачесть.

Но в любом случае спасибо за данный маннуал, лишним от точно не будет.
 
Все не чего, только статьи на конкурс принимались до 29 августа, могут не зачесть.

Но в любом случае спасибо за данный маннуал, лишним от точно не будет.
До 29-го включительно принимались. Отправлена на модерацию была статья именно в ту дату 29-го вечером

Скрипты тоооочно не написанны нейронкой)
Это не скрипты, это вырезки из моего полноценного решения полностью функционирующего с большой базой кода.
Сообщение обновлено:

Вот какое кол-во шаблонов админки например
Снимок экрана от 2025-09-02 14-35-36.png


Вот часть исходника проверки платежей через tron блокчейн. К слову проверяется также адрес смарт контракта для безопасности, чтобы избежать проблем с отправкой юзерами не usdt официального.
Снимок экрана от 2025-09-02 14-37-05.png
 
Последнее редактирование:
Удачи на конкурсе бро:)
 
Это не скрипты, это вырезки из моего полноценного решения полностью функционирующего с большой базой кода.
Статья прикольная, но лучше реализовать было в docker образе чтобы собрал единый проект изолировано, venv не очень стабилен в таких проектах.
Насчёт что это не нейронкой сделано могу поспорить, я в replit на flask уже делал подобную реализацию. Он схожие решения предлагает. Но не запрещено делать продукт нейронкой, сам текст и подача запрещена нейронкой.
На днях затещу по твоему гайду админку, скажу как оно. Выглядит вроде цевильно. Ток чёт не заметил в коде реализацию платежей, хотя бы btc и выбор языка лишняя функция. Ну и подписать каждый файл конфигурацию было бы прикольно, а то у тебя просто коды вставки, а че за файл конфига неизвестно, так сказать угадывай из структуры проекта по коду) Ещё совет добавить функцию логирования команд и функций. Допустим если на каком то этапе будет ошибка или косяк легче по логам найти что где не так.
Сообщение обновлено:

Скрипты тоооочно не написанны нейронкой)
а в чём хейт нейронок? они щас без особого знания программирования помогают создать проект лучше любого джуна и мидла с опытом 5 лет
 
Удачи на конкурсе бро:)
Спасибо!

но лучше реализовать было в docker образе чтобы собрал единый проект изолировано, venv не очень стабилен в таких проектах.
Обычно не использую docker. Ибо хватает зависимостей и версий virtualenv по крайней мере в средних проектах.

Ток чёт не заметил в коде реализацию платежей, хотя бы btc и выбор языка лишняя функция
Я там отдельным сообщением потом приложил пример usdt trc-20
Тут расписано не полностью, а основы проекта и части кода, потому что кодовая база на самом деле куда больше, очень длинно бы получилось. Для разьяснения в теме, если что нахожусь.
 
Здравствуйте. Редко участвую в конкурсах, однако вот.

Решил расписать вам технологию изготовления мной, специального решения автопродаж. Написано с нуля, и решает все основные вопросы, которые нужны для подобного типа решений.Это будут бот(ы) для телеграм и удобная веб админка для управления всем функционалом.
Присутствует защита от блокировок. А также рассылка от ботов, и от юзеров,чтобы сохранять клиентов всегда. Оплата принимается ботов на автомате с помощью usdt trc-20.

И другие плюшки!


Глава 1. Инструментарий.

В качестве основного языка взят, идеальный на мой взгляд язык – python. Он сочетает в себе простоту синтаксиса, мощь открытых библиотек и скорость разработки. На самом деле на нем очень хорошо писать веб решения так, как он интерпретируем и имеет множество идеальных заготовок.

Библиотеки flask sqlalchemy, jinja2, pyTelegramBotAPI, telethon.

Основные файлы:​

  • app.py - Flask веб-приложение с админ-панелью
  • bot_handlers.py - Обработчики Telegram-бота
  • models.py - Модели базы данных SQLAlchemy
  • run.py - Точка входа для запуска приложения
  • tron_payment_checker.py - Проверка USDT платежей
  • user_broadcast.py - Система массовых рассылок

Конфигурация:​

  • requirements.txt - Зависимости Python
  • .env.example - Пример файла окружения

Глава 2. Программируем.

В качестве основного языка разработки был использован python, и его микрофреймворк flask. Внутри также разработка на шаблонах jinja2 (тот же html,только с удобными тегами автоматизации) и далее css,js для красивостей.
Для базы данных стоит по умолчанию sqlite, но так как используется система sqlalchemy, то можно просто в конфиге поменять и на более мощные при желании базы mysql,postgresql.

Сначала нужно инициализировать проект.

# Создание виртуального окружения
Код:
python3 -m venv .
source bin/activate  # Linux/Mac
# или
# Scripts\activate  # Windows

# Установка зависимостей
pip install Flask==2.3.3 Flask-SQLAlchemy==3.0.5 Flask-Migrate==4.0.5 pyTelegramBotAPI==4.14.0 python-dotenv==1.0.0 qrcode==7.4.2 Pillow==10.0.1 Telethon==1.34.0

Создать файл конфиг
Код:
# Секретный ключ для Flask

SECRET_KEY=your-very-secret-key-here-change-this-in-production
# URL базы данных

DATABASE_URL=sqlite:///bot.db

# Режим Flask

FLASK_ENV=development

# Порт и хост

FLASK_PORT=5000
FLASK_HOST=0.0.0.0

Создать модели базы данных. Написано коротко, не все написал чтобы не засорять статью. Пример юзер модели добавил.

Код:
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import json

# Создаем объект db
db = SQLAlchemy()

class User(db.Model):

"""Модель для хранения информации о пользователях"""

__tablename__ = 'users'

id = db.Column(db.Integer, primary_key=True)
telegram_id = db.Column(db.BigInteger, nullable=False)
bot_id = db.Column(db.Integer, db.ForeignKey('bots.id'), nullable=False)
username = db.Column(db.String(100), nullable=True)
first_name = db.Column(db.String(100), nullable=True)
last_name = db.Column(db.String(100), nullable=True)
phone_number = db.Column(db.String(20), nullable=True)
language_code = db.Column(db.String(10), nullable=True)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_activity = db.Column(db.DateTime, default=datetime.utcnow)

# Уникальность по telegram_id и bot_id

__table_args__ = (db.UniqueConstraint('telegram_id', 'bot_id', name='unique_user_bot'),)

def __repr__(self):
return f'<User {self.telegram_id}>'
Создаем flask приложение app.py
Код:
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session
from flask_migrate import Migrate
from functools import wraps

import os
from datetime import datetime
import threading

import telebot

import json

import logging

import hashlib



# Настройка логирования

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)



app = Flask(__name__)

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'your-secret-key-here')

app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///bot.db')

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False



# Импорт db из models

from models import db, Bot, User, Setting, Address



# Инициализация db с приложением

db.init_app(app)

migrate = Migrate(app, db)



# Словарь активных ботов

active_bots = {}



# Функции аутентификации

def get_admin_password():

"""Получить пароль администратора из настроек"""

setting = Setting.query.filter_by(key='admin_password').first()

return setting.value if setting else ''



def hash_password(password):

"""Хеширование пароля"""

return hashlib.sha256(password.encode()).hexdigest()



def check_password(password):

"""Проверка пароля"""

stored_password = get_admin_password()

if not stored_password:

return password == ''

return hash_password(password) == stored_password



def login_required(f):

"""Декоратор для проверки авторизации"""

@wraps(f)

def decorated_function(*args, **kwargs):

if not session.get('authenticated'):

return redirect(url_for('login'))

return f(*args, **kwargs)

return decorated_function



@app.route('/login', methods=['GET', 'POST'])

def login():

"""Страница входа"""

if request.method == 'POST':

password = request.form.get('password', '')



if check_password(password):

session['authenticated'] = True

flash('Успешный вход в систему!', 'success')

return redirect(url_for('index'))

else:

flash('Неверный пароль!', 'error')



return render_template('login.html')



@app.route('/logout')

def logout():

"""Выход из системы"""

session.pop('authenticated', None)

flash('Вы вышли из системы', 'info')

return redirect(url_for('login'))



@app.route('/')

@login_required

def index():

"""Главная страница админ-панели"""

bots = Bot.query.all()

users_count = User.query.count()

active_users_count = User.query.filter_by(is_active=True).count()



return render_template('index.html',

bots=bots,

users_count=users_count,

active_users_count=active_users_count)



@app.route('/bots')

@login_required

def bots():

"""Страница управления ботами"""

bots = Bot.query.all()

return render_template('bots.html', bots=bots)



@app.route('/users')

@login_required

def users():

"""Страница управления пользователями"""

users = User.query.all()

return render_template('users.html', users=users)



if __name__ == '__main__':

app.run(debug=True)
Далее нужно создать файл запуска run.py и файлы шаблонов html jinja2 в папке templates для описания интерфейса админки.

Код:
#!/usr/bin/env python3



import os

from dotenv import load_dotenv



# Загружаем переменные окружения

load_dotenv()



from app import app, db



if __name__ == '__main__':

# Создаем таблицы базы данных

with app.app_context():

db.create_all()

print("База данных инициализирована")



# Получаем настройки

host = os.environ.get('FLASK_HOST', '0.0.0.0')

port = int(os.environ.get('FLASK_PORT', 5000))

debug = os.environ.get('FLASK_ENV', 'production') == 'development'



print(f"Запуск Bot на {host}:{port}")

print(f"Веб-админка: http://{host}:{port}")



# Запускаем приложение

app.run(host=host, port=port, debug=debug)
[CODE]

[CENTER][ATTACH type="full" width="828px" size="1199x499"]2271599[/ATTACH]
Основная страница админки

[/CENTER]

Каждый бот запускается в отдельном потоке,что удобно и безопасно, и позволяет управлять целой армией ботов.
[CODE]
def run_bot():

try:

telegram_bot.polling(none_stop=True)

except Exception as e:

logger.error(f"Ошибка в потоке бота {bot_id}: {e}")



bot_thread = threading.Thread(target=run_bot)

bot_thread.daemon = True

bot_thread.start()


Учтите при,что все операции с базой необходимо выполнять в контексте app.

with app.app_context():

user = get_or_create_user(message, bot_id)

# работа с базой данных

Для безопасности было реализовано,что админка доступна лишь с локалхоста, то есть с тор домена. И защищена паролем.
Также бот помнит на каком этапе находится каждый из пользователей благодаря системе сессий.

Также сам бот было решено создать многоязычным поэтому реализована система переводов внутри русский и грузинский, вместе с любыми параметрами. В коде существует словарь TEXTS с переводами на русский и грузинский языки, содержащий более 100 текстовых строк. Это не просто набор фраз — это продуманная система, где каждое сообщение имеет свой ключ и может содержать параметры для подстановки.

'order_created': '✅ Заказ #{order_id} создан!\n\n Сумма: ${amount} USDT\n Адрес для оплаты:\n\n`{address}`'







Посмотреть вложение 2271601

Рассылка через ботов также возможна​

Реализация обработчиков бота в телеграм.
Код:
from telebot import types

from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton

import json

import os

from datetime import datetime

from models import db, User, Bot, Setting

import logging



logger = logging.getLogger(__name__)



# Словари для локализации

TEXTS = {

'ru': {

'welcome': ' Добро пожаловать! Выберите язык:',

'main_menu': ' Главное меню',

'language_selected': '✅ Язык выбран: Русский',

'contact_operator': '‍ Связаться с оператором',

'catalog': '️ Каталог товаров',

'my_orders': ' Мои заказы',

'settings': '⚙️ Настройки',

'change_language': ' Изменить язык',

},

'en': {

'welcome': ' Welcome! Choose your language:',

'main_menu': ' Main menu',

'language_selected': '✅ Language selected: English',

'contact_operator': '‍ Contact operator',

'catalog': '️ Product catalog',

'my_orders': ' My orders',

'settings': '⚙️ Settings',

'change_language': ' Change language',

}

}



def get_text(user_lang, key):

"""Получить текст на языке пользователя"""

return TEXTS.get(user_lang, TEXTS['ru']).get(key, key)



def create_main_menu_keyboard(user_lang='ru'):

"""Создать главное меню"""

keyboard = InlineKeyboardMarkup(row_width=2)



buttons = [

InlineKeyboardButton(get_text(user_lang, 'catalog'), callback_data='catalog'),

InlineKeyboardButton(get_text(user_lang, 'my_orders'), callback_data='my_orders'),

InlineKeyboardButton(get_text(user_lang, 'contact_operator'), callback_data='contact_operator'),

InlineKeyboardButton(get_text(user_lang, 'settings'), callback_data='settings'),

]



keyboard.add(*buttons)

return keyboard



def create_language_keyboard():

"""Создать клавиатуру выбора языка"""

keyboard = InlineKeyboardMarkup(row_width=2)

keyboard.add(

InlineKeyboardButton(' Русский', callback_data='lang_ru'),

InlineKeyboardButton(' English', callback_data='lang_en')

)

return keyboard



def create_bot_handlers(bot, bot_model):

"""Создать обработчики для бота"""



@bot.message_handler(commands=['start'])

def handle_start(message):

"""Обработчик команды /start"""

try:

# Получаем или создаем пользователя

user = User.query.filter_by(

telegram_id=message.from_user.id,

bot_id=bot_model.id

).first()



if not user:

user = User(

telegram_id=message.from_user.id,

bot_id=bot_model.id,

username=message.from_user.username,

first_name=message.from_user.first_name,

last_name=message.from_user.last_name,

language_code=message.from_user.language_code or 'ru'

)

db.session.add(user)

db.session.commit()



# Обновляем активность

user.last_activity = datetime.utcnow()

db.session.commit()



# Отправляем приветствие

bot.send_message(

message.chat.id,

get_text(user.language_code, 'welcome'),

reply_markup=create_language_keyboard()

)



except Exception as e:

logger.error(f"Ошибка в handle_start: {e}")

bot.send_message(message.chat.id, "Произошла ошибка. Попробуйте позже.")



@bot.callback_query_handler(func=lambda call: call.data.startswith('lang_'))

def handle_language_selection(call):

"""Обработчик выбора языка"""

try:

lang = call.data.split('_')[1]



# Обновляем язык пользователя

user = User.query.filter_by(

telegram_id=call.from_user.id,

bot_id=bot_model.id

).first()



if user:

user.language_code = lang

db.session.commit()



# Отправляем главное меню

bot.edit_message_text(

get_text(lang, 'language_selected'),

call.message.chat.id,

call.message.message_id

)



bot.send_message(

call.message.chat.id,

get_text(lang, 'main_menu'),

reply_markup=create_main_menu_keyboard(lang)

)



except Exception as e:

logger.error(f"Ошибка в handle_language_selection: {e}")



@bot.callback_query_handler(func=lambda call: True)

def handle_callback_query(call):

"""Обработчик callback запросов"""

try:

user = User.query.filter_by(

telegram_id=call.from_user.id,

bot_id=bot_model.id

).first()



if not user:

return



user_lang = user.language_code or 'ru'



if call.data == 'catalog':

bot.answer_callback_query(call.id, "Каталог в разработке")

elif call.data == 'my_orders':

bot.answer_callback_query(call.id, "Заказы в разработке")

elif call.data == 'contact_operator':

bot.answer_callback_query(call.id, "Связь с оператором в разработке")

elif call.data == 'settings':

bot.edit_message_text(

get_text(user_lang, 'settings'),

call.message.chat.id,

call.message.message_id,

reply_markup=create_language_keyboard()

)



except Exception as e:

logger.error(f"Ошибка в handle_callback_query: {e}")



return bot


Ниже приложил пример реализации самого интерфейса бота в тг.



Глава 3. Тестируем, размышляем, дополняем.



Для защит от различных блокировок конкурентов, переборов и прочих нюансов было решено придумать удобные решения. Путем анализа других ботов и проверки своего создали некоторые моменты, которые решают большинство проблем.

А именно:
  1. Задержки между сообщениями:
Код:
    # Небольшая задержка между отправками
    await asyncio.sleep(1)
  • Ограничения в интерфейсе веб-админки
Код:
<li class="mb-2">

 
<i class="fas fa-check text-success"></i>

 
<small>Не отправляйте более 30 сообщений в минуту</small>

 
</li>


  • Обработка ошибок
Код:
try:

 
await client.send_message(entity, message)

 
sent_count += 1

 
except Exception as e:

 
failed_count += 1

 
errors.append(f"{target}: {str(e)}")

  • Система капчи для проверки при входе в бота юзера
Код:
def create_captcha_for_user(user_id, bot_id):

 
# Генерируем случайные эмодзи

 
all_emojis = ['', '', '', '⭐', '', '', '', '']

 
target_emoji = random.choice(all_emojis)

 

 
# Создаем варианты ответов

 
emoji_options = [target_emoji]

 
while len(emoji_options) < 6:

 
emoji = random.choice(all_emojis)

 
If emoji not in emoji_options:

 
emoji_options.append(emoji)

 

 
random.shuffle(emoji_options)


  • Возможность запуска множества ботов одновременно. В админке можно по нажатию клавиш и форм создать множество ботов и включать их в разных потоках независимо.

Посмотреть вложение 2271610

Код:
# Словарь активных ботов

 
active_bots = {}

 

 
@app.route('/start_bot/<int:bot_id>')

 
def start_bot(bot_id):

 
# Каждый бот запускается в отдельном потоке

 
bot_thread = threading.Thread(target=run_bot)

 
bot_thread.daemon = True

 
bot_thread.start()

 

 
active_bots[bot_id] = {

 
'bot': telegram_bot,

 
'thread': bot_thread

 
}




  • Самая мощная функция — рассылки через пользовательские аккаунты с помощью telethon. Это обходит многие ограничения обычного апи тг. Нужно для того,чтобы когда заблокируют одного бота можно было разослать старым пользователям через обычного юзера оповещение о новых ботах или каналах,форумах. Таким образом ваши клиенты всегда при вас. Отправа идет по username, или номеру если у пользователя нет ника.

Код:
class UserBroadcastManager:

 
async def send_broadcast(self, phone, message, targets, api_id, api_hash):

 
client = TelegramClient(session_file, api_id, api_hash)

 

 
for target in targets:

 
try:

 
# Определяем тип цели (username или номер)

 
if target.startswith('@'):

 
entity = await client.get_entity(target)

 
elif target.startswith('+') or target.isdigit():

 
entity = await client.get_entity(target)

 

 
await client.send_message(entity, message)

 
sent_count += 1

 

 
# Задержка между отправками

 
await asyncio.sleep(1)

 

 
except Exception as e:

 
failed_count += 1

 
Можно легко добавлять аккаунты пользователей(для рассылки) в админке и получать их сессии вписав код с тг.

 
function startAuth() {

 
// Двухэтапная авторизация

 
// 1. Отправка кода на телефон

 
// 2. Подтверждение кода (+ 2FA если нужно)

 

 
fetch('/user_broadcast/auth', {

 
method: 'POST',

 
body: formData

 
})

 
.then(response => response.json())

 
.then(data => {

 
if (data.success) {

 
// Переход ко второму шагу

 
showCodeInput();

 
}

 
});

 
}




  • Особенно элегантно решена задача с QR-кодами для платежей. Функция generate_qr_code() не только создает QR-код с настраиваемым дизайном (цвета, размер, логотип), но и schedule_qr_deletion() автоматически удаляет его через заданное время. Это повышает безопасность для ваших тормознутых клиентов.

  • Колесо фортуны. Функцию работающая через сайт и выдающая коды бонусы и скидки.








  • Одна из самых интересных функций get_smart_stash(). Это не просто выбор случайного товара со склада. Система анализирует доступные клады и выбирает оптимальный по нескольким критериям:

Приоритет по весу и расположению — сначала ищет точное совпадение
Временные факторы — предпочитает более старые клады

Это обеспечивает эффективную ротацию товаров и оптимизацию логистики. Очень удобно при автоматических продажах
Код:
def get_smart_stash(product_id, weight):

 
"""

 
Получить клад с умной логикой распределения.

 
Выбирает клады с разницей по загрузке минимум в 5 позиций.

 
Логика: 1-й, 6-й, 11-й, 16-й, потом 2-й, 7-й, 12-й, 3-й, 8-й и т.д.

 
"""

 
try:

 
# Получаем все доступные клады для данного продукта и веса, отсортированные по ID (порядок загрузки)

 
available_stashes = Stash.query.filter_by(

 
product_id=product_id,

 
weight=weight,

 
is_sold=False,

 
order_item_id=None

 
).order_by(Stash.id).all()

 

 
if not available_stashes:

 
return None

 

 
# Если кладов меньше 5, просто берем первый

 
if len(available_stashes) < 5:

 
return available_stashes[0]

 

 
# Получаем ключ для отслеживания текущего паттерна выбора

 
# Используем комбинацию product_id и weight как ключ

 
pattern_key = f"stash_pattern_{product_id}_{weight}"

 

 
# Получаем или создаем настройку для отслеживания паттерна

 
pattern_setting = Setting.query.filter_by(key=pattern_key).first()

 
if not pattern_setting:

 
pattern_setting = Setting(key=pattern_key, value="0,0")  # current_group, position_in_group

 
db.session.add(pattern_setting)

 
db.session.commit()

 

 
# Парсим текущее состояние паттерна

 
try:

 
current_group, position_in_group = map(int, pattern_setting.value.split(','))

 
except:

 
current_group, position_in_group = 0, 0

 

 
# Вычисляем индекс клада для выбора

 
# Группы: 0 = позиции 0,5,10,15... | 1 = позиции 1,6,11,16... | и т.д.

 
stash_index = current_group + (position_in_group * 5)

 

 
# Если индекс выходит за границы, переходим к следующей группе

 
if stash_index >= len(available_stashes):

 
current_group += 1

 
position_in_group = 0

 

 
# Если все группы пройдены, начинаем сначала

 
if current_group >= 5:

 
current_group = 0

 
position_in_group = 0

 

 
stash_index = current_group + (position_in_group * 5)

 

 
# Если все еще выходит за границы, берем первый доступный

 
if stash_index >= len(available_stashes):

 
stash_index = 0

 
current_group = 0

 
position_in_group = 0

 

 
# Выбираем клад

 
selected_stash = available_stashes[stash_index]

 

 
# Обновляем паттерн для следующего выбора

 
position_in_group += 1

 

 
# Если достигли конца текущей группы, переходим к следующей

 
max_positions_in_group = (len(available_stashes) - current_group + 4) // 5  # Округление вверх

 
if position_in_group >= max_positions_in_group:

 
current_group += 1

 
position_in_group = 0

 

 
# Если все группы пройдены, начинаем сначала

 
if current_group >= 5:

 
current_group = 0

 

 
# Сохраняем обновленный паттерн

 
pattern_setting.value = f"{current_group},{position_in_group}"

 
db.session.commit()

 

 
logger.info(f"Выбран клад ID={selected_stash.id} (индекс {stash_index} из {len(available_stashes)}) для продукта {product_id}, вес {weight}")

 

 
return selected_stash

 

 
except Exception as e:

 
logger.error(f"Ошибка в get_smart_stash: {str(e)}")

 
# В случае ошибки возвращаем первый доступный клад

 
return Stash.query.filter_by(

 
product_id=product_id,

 
weight=weight,

 
is_sold=False,

 
order_item_id=None

 
).first()

Послесловие:


Как вы видите нет ничего сложного в своем собственном решении для автопродаж.
При прямых руках и желании вы можете создать себе независимый от сторонних программных решений автоматизированный бизнес с ботами и админкой.

Если будут дополнительные вопросы по реализации подобных решений или конкретным интересным идеям, пишите в вопросы к статье, помогу чем смогу.
Спасибо за внимание!!! Процветания вашим проектам и бизнесам!​
Хорошая у тебя тема, единственное НО, новичкам будет сложно понять !
 
Выглядит словно вы свой бизнес проект продвигаете как статью,
и ещё бабки за это хотите, имхо.
 
Статья прикольная, но лучше реализовать было в docker образе чтобы собрал единый проект изолировано, venv не очень стабилен в таких проектах.
Насчёт что это не нейронкой сделано могу поспорить, я в replit на flask уже делал подобную реализацию. Он схожие решения предлагает. Но не запрещено делать продукт нейронкой, сам текст и подача запрещена нейронкой.
На днях затещу по твоему гайду админку, скажу как оно. Выглядит вроде цевильно. Ток чёт не заметил в коде реализацию платежей, хотя бы btc и выбор языка лишняя функция. Ну и подписать каждый файл конфигурацию было бы прикольно, а то у тебя просто коды вставки, а че за файл конфига неизвестно, так сказать угадывай из структуры проекта по коду) Ещё совет добавить функцию логирования команд и функций. Допустим если на каком то этапе будет ошибка или косяк легче по логам найти что где не так.
Сообщение обновлено:


а в чём хейт нейронок? они щас без особого знания программирования помогают создать проект лучше любого джуна и мидла с опытом 5 лет
Ещё забавно, сколько новорегов в комментариях) Которые проявляют актив исключительно в теме с конкурсом
Сообщение обновлено:

Спасибо!


Обычно не использую docker. Ибо хватает зависимостей и версий virtualenv по крайней мере в средних проектах.


Я там отдельным сообщением потом приложил пример usdt trc-20
Тут расписано не полностью, а основы проекта и части кода, потому что кодовая база на самом деле куда больше, очень длинно бы получилось. Для разьяснения в теме, если что нахожусь.
Сам написал - сам ответил)
 
Хорошая у тебя тема, единственное НО, новичкам будет сложно понять !
Поэтому можно вопросы в теме задать если что,добавлю кода или обьясню нюансы.

Выглядит словно вы свой бизнес проект продвигаете как статью,
и ещё бабки за это хотите, имхо.
Вы хоть раз видели обучающую статью на подобную тему. Я нет) Теперь существует

Ещё забавно, сколько новорегов в комментариях) Которые проявляют актив исключительно в теме с конкурсом
О боже) тут шерлок холмс

Сам написал - сам ответил)
У вас какая-то обида на что-то?) может стоит последить за своими "статьями"?))
В теме, где вам нечего по существу сказать по данной тематике, а оно видно,лучше не пишите.
 
Поэтому можно вопросы в теме задать если что,добавлю кода или обьясню нюансы.


Вы хоть раз видели обучающую статью на подобную тему. Я нет) Теперь существует


О боже) тут шерлок холмс


У вас какая-то обида на что-то?) может стоит последить за своими "статьями"?))
В теме, где вам нечего по существу сказать по данной тематике, а оно видно,лучше не пишите.
Забей, я ничего доказывать не планировал) Просто забавно выглядит
 
круто!это труд!
 
Здравствуйте. Редко участвую в конкурсах, однако вот.

Решил расписать вам технологию изготовления мной, специального решения автопродаж. Написано с нуля, и решает все основные вопросы, которые нужны для подобного типа решений.Это будут бот(ы) для телеграм и удобная веб админка для управления всем функционалом.
Присутствует защита от блокировок. А также рассылка от ботов, и от юзеров,чтобы сохранять клиентов всегда. Оплата принимается ботов на автомате с помощью usdt trc-20.

И другие плюшки!


Глава 1. Инструментарий.

В качестве основного языка взят, идеальный на мой взгляд язык – python. Он сочетает в себе простоту синтаксиса, мощь открытых библиотек и скорость разработки. На самом деле на нем очень хорошо писать веб решения так, как он интерпретируем и имеет множество идеальных заготовок.

Библиотеки flask sqlalchemy, jinja2, pyTelegramBotAPI, telethon.

Основные файлы:​

  • app.py - Flask веб-приложение с админ-панелью
  • bot_handlers.py - Обработчики Telegram-бота
  • models.py - Модели базы данных SQLAlchemy
  • run.py - Точка входа для запуска приложения
  • tron_payment_checker.py - Проверка USDT платежей
  • user_broadcast.py - Система массовых рассылок

Конфигурация:​

  • requirements.txt - Зависимости Python
  • .env.example - Пример файла окружения

Глава 2. Программируем.

В качестве основного языка разработки был использован python, и его микрофреймворк flask. Внутри также разработка на шаблонах jinja2 (тот же html,только с удобными тегами автоматизации) и далее css,js для красивостей.
Для базы данных стоит по умолчанию sqlite, но так как используется система sqlalchemy, то можно просто в конфиге поменять и на более мощные при желании базы mysql,postgresql.

Сначала нужно инициализировать проект.

# Создание виртуального окружения
Код:
python3 -m venv .
source bin/activate  # Linux/Mac
# или
# Scripts\activate  # Windows

# Установка зависимостей
pip install Flask==2.3.3 Flask-SQLAlchemy==3.0.5 Flask-Migrate==4.0.5 pyTelegramBotAPI==4.14.0 python-dotenv==1.0.0 qrcode==7.4.2 Pillow==10.0.1 Telethon==1.34.0

Создать файл конфиг
Код:
# Секретный ключ для Flask

SECRET_KEY=your-very-secret-key-here-change-this-in-production
# URL базы данных

DATABASE_URL=sqlite:///bot.db

# Режим Flask

FLASK_ENV=development

# Порт и хост

FLASK_PORT=5000
FLASK_HOST=0.0.0.0

Создать модели базы данных. Написано коротко, не все написал чтобы не засорять статью. Пример юзер модели добавил.

Код:
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import json

# Создаем объект db
db = SQLAlchemy()

class User(db.Model):

"""Модель для хранения информации о пользователях"""

__tablename__ = 'users'

id = db.Column(db.Integer, primary_key=True)
telegram_id = db.Column(db.BigInteger, nullable=False)
bot_id = db.Column(db.Integer, db.ForeignKey('bots.id'), nullable=False)
username = db.Column(db.String(100), nullable=True)
first_name = db.Column(db.String(100), nullable=True)
last_name = db.Column(db.String(100), nullable=True)
phone_number = db.Column(db.String(20), nullable=True)
language_code = db.Column(db.String(10), nullable=True)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_activity = db.Column(db.DateTime, default=datetime.utcnow)

# Уникальность по telegram_id и bot_id

__table_args__ = (db.UniqueConstraint('telegram_id', 'bot_id', name='unique_user_bot'),)

def __repr__(self):
return f'<User {self.telegram_id}>'
Создаем flask приложение app.py
Код:
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session
from flask_migrate import Migrate
from functools import wraps

import os
from datetime import datetime
import threading

import telebot

import json

import logging

import hashlib



# Настройка логирования

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)



app = Flask(__name__)

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'your-secret-key-here')

app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///bot.db')

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False



# Импорт db из models

from models import db, Bot, User, Setting, Address



# Инициализация db с приложением

db.init_app(app)

migrate = Migrate(app, db)



# Словарь активных ботов

active_bots = {}



# Функции аутентификации

def get_admin_password():

"""Получить пароль администратора из настроек"""

setting = Setting.query.filter_by(key='admin_password').first()

return setting.value if setting else ''



def hash_password(password):

"""Хеширование пароля"""

return hashlib.sha256(password.encode()).hexdigest()



def check_password(password):

"""Проверка пароля"""

stored_password = get_admin_password()

if not stored_password:

return password == ''

return hash_password(password) == stored_password



def login_required(f):

"""Декоратор для проверки авторизации"""

@wraps(f)

def decorated_function(*args, **kwargs):

if not session.get('authenticated'):

return redirect(url_for('login'))

return f(*args, **kwargs)

return decorated_function



@app.route('/login', methods=['GET', 'POST'])

def login():

"""Страница входа"""

if request.method == 'POST':

password = request.form.get('password', '')



if check_password(password):

session['authenticated'] = True

flash('Успешный вход в систему!', 'success')

return redirect(url_for('index'))

else:

flash('Неверный пароль!', 'error')



return render_template('login.html')



@app.route('/logout')

def logout():

"""Выход из системы"""

session.pop('authenticated', None)

flash('Вы вышли из системы', 'info')

return redirect(url_for('login'))



@app.route('/')

@login_required

def index():

"""Главная страница админ-панели"""

bots = Bot.query.all()

users_count = User.query.count()

active_users_count = User.query.filter_by(is_active=True).count()



return render_template('index.html',

bots=bots,

users_count=users_count,

active_users_count=active_users_count)



@app.route('/bots')

@login_required

def bots():

"""Страница управления ботами"""

bots = Bot.query.all()

return render_template('bots.html', bots=bots)



@app.route('/users')

@login_required

def users():

"""Страница управления пользователями"""

users = User.query.all()

return render_template('users.html', users=users)



if __name__ == '__main__':

app.run(debug=True)
Далее нужно создать файл запуска run.py и файлы шаблонов html jinja2 в папке templates для описания интерфейса админки.

Код:
#!/usr/bin/env python3



import os

from dotenv import load_dotenv



# Загружаем переменные окружения

load_dotenv()



from app import app, db



if __name__ == '__main__':

# Создаем таблицы базы данных

with app.app_context():

db.create_all()

print("База данных инициализирована")



# Получаем настройки

host = os.environ.get('FLASK_HOST', '0.0.0.0')

port = int(os.environ.get('FLASK_PORT', 5000))

debug = os.environ.get('FLASK_ENV', 'production') == 'development'



print(f"Запуск Bot на {host}:{port}")

print(f"Веб-админка: http://{host}:{port}")



# Запускаем приложение

app.run(host=host, port=port, debug=debug)
[CODE]

[CENTER][ATTACH type="full" width="828px" size="1199x499"]2271599[/ATTACH]
Основная страница админки

[/CENTER]

Каждый бот запускается в отдельном потоке,что удобно и безопасно, и позволяет управлять целой армией ботов.
[CODE]
def run_bot():

try:

telegram_bot.polling(none_stop=True)

except Exception as e:

logger.error(f"Ошибка в потоке бота {bot_id}: {e}")



bot_thread = threading.Thread(target=run_bot)

bot_thread.daemon = True

bot_thread.start()


Учтите при,что все операции с базой необходимо выполнять в контексте app.

with app.app_context():

user = get_or_create_user(message, bot_id)

# работа с базой данных

Для безопасности было реализовано,что админка доступна лишь с локалхоста, то есть с тор домена. И защищена паролем.
Также бот помнит на каком этапе находится каждый из пользователей благодаря системе сессий.

Также сам бот было решено создать многоязычным поэтому реализована система переводов внутри русский и грузинский, вместе с любыми параметрами. В коде существует словарь TEXTS с переводами на русский и грузинский языки, содержащий более 100 текстовых строк. Это не просто набор фраз — это продуманная система, где каждое сообщение имеет свой ключ и может содержать параметры для подстановки.

'order_created': '✅ Заказ #{order_id} создан!\n\n Сумма: ${amount} USDT\n Адрес для оплаты:\n\n`{address}`'







Посмотреть вложение 2271601

Рассылка через ботов также возможна​

Реализация обработчиков бота в телеграм.
Код:
from telebot import types

from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton

import json

import os

from datetime import datetime

from models import db, User, Bot, Setting

import logging



logger = logging.getLogger(__name__)



# Словари для локализации

TEXTS = {

'ru': {

'welcome': ' Добро пожаловать! Выберите язык:',

'main_menu': ' Главное меню',

'language_selected': '✅ Язык выбран: Русский',

'contact_operator': '‍ Связаться с оператором',

'catalog': '️ Каталог товаров',

'my_orders': ' Мои заказы',

'settings': '⚙️ Настройки',

'change_language': ' Изменить язык',

},

'en': {

'welcome': ' Welcome! Choose your language:',

'main_menu': ' Main menu',

'language_selected': '✅ Language selected: English',

'contact_operator': '‍ Contact operator',

'catalog': '️ Product catalog',

'my_orders': ' My orders',

'settings': '⚙️ Settings',

'change_language': ' Change language',

}

}



def get_text(user_lang, key):

"""Получить текст на языке пользователя"""

return TEXTS.get(user_lang, TEXTS['ru']).get(key, key)



def create_main_menu_keyboard(user_lang='ru'):

"""Создать главное меню"""

keyboard = InlineKeyboardMarkup(row_width=2)



buttons = [

InlineKeyboardButton(get_text(user_lang, 'catalog'), callback_data='catalog'),

InlineKeyboardButton(get_text(user_lang, 'my_orders'), callback_data='my_orders'),

InlineKeyboardButton(get_text(user_lang, 'contact_operator'), callback_data='contact_operator'),

InlineKeyboardButton(get_text(user_lang, 'settings'), callback_data='settings'),

]



keyboard.add(*buttons)

return keyboard



def create_language_keyboard():

"""Создать клавиатуру выбора языка"""

keyboard = InlineKeyboardMarkup(row_width=2)

keyboard.add(

InlineKeyboardButton(' Русский', callback_data='lang_ru'),

InlineKeyboardButton(' English', callback_data='lang_en')

)

return keyboard



def create_bot_handlers(bot, bot_model):

"""Создать обработчики для бота"""



@bot.message_handler(commands=['start'])

def handle_start(message):

"""Обработчик команды /start"""

try:

# Получаем или создаем пользователя

user = User.query.filter_by(

telegram_id=message.from_user.id,

bot_id=bot_model.id

).first()



if not user:

user = User(

telegram_id=message.from_user.id,

bot_id=bot_model.id,

username=message.from_user.username,

first_name=message.from_user.first_name,

last_name=message.from_user.last_name,

language_code=message.from_user.language_code or 'ru'

)

db.session.add(user)

db.session.commit()



# Обновляем активность

user.last_activity = datetime.utcnow()

db.session.commit()



# Отправляем приветствие

bot.send_message(

message.chat.id,

get_text(user.language_code, 'welcome'),

reply_markup=create_language_keyboard()

)



except Exception as e:

logger.error(f"Ошибка в handle_start: {e}")

bot.send_message(message.chat.id, "Произошла ошибка. Попробуйте позже.")



@bot.callback_query_handler(func=lambda call: call.data.startswith('lang_'))

def handle_language_selection(call):

"""Обработчик выбора языка"""

try:

lang = call.data.split('_')[1]



# Обновляем язык пользователя

user = User.query.filter_by(

telegram_id=call.from_user.id,

bot_id=bot_model.id

).first()



if user:

user.language_code = lang

db.session.commit()



# Отправляем главное меню

bot.edit_message_text(

get_text(lang, 'language_selected'),

call.message.chat.id,

call.message.message_id

)



bot.send_message(

call.message.chat.id,

get_text(lang, 'main_menu'),

reply_markup=create_main_menu_keyboard(lang)

)



except Exception as e:

logger.error(f"Ошибка в handle_language_selection: {e}")



@bot.callback_query_handler(func=lambda call: True)

def handle_callback_query(call):

"""Обработчик callback запросов"""

try:

user = User.query.filter_by(

telegram_id=call.from_user.id,

bot_id=bot_model.id

).first()



if not user:

return



user_lang = user.language_code or 'ru'



if call.data == 'catalog':

bot.answer_callback_query(call.id, "Каталог в разработке")

elif call.data == 'my_orders':

bot.answer_callback_query(call.id, "Заказы в разработке")

elif call.data == 'contact_operator':

bot.answer_callback_query(call.id, "Связь с оператором в разработке")

elif call.data == 'settings':

bot.edit_message_text(

get_text(user_lang, 'settings'),

call.message.chat.id,

call.message.message_id,

reply_markup=create_language_keyboard()

)



except Exception as e:

logger.error(f"Ошибка в handle_callback_query: {e}")



return bot


Ниже приложил пример реализации самого интерфейса бота в тг.



Глава 3. Тестируем, размышляем, дополняем.



Для защит от различных блокировок конкурентов, переборов и прочих нюансов было решено придумать удобные решения. Путем анализа других ботов и проверки своего создали некоторые моменты, которые решают большинство проблем.

А именно:
  1. Задержки между сообщениями:
Код:
    # Небольшая задержка между отправками
    await asyncio.sleep(1)
  • Ограничения в интерфейсе веб-админки
Код:
<li class="mb-2">

 
<i class="fas fa-check text-success"></i>

 
<small>Не отправляйте более 30 сообщений в минуту</small>

 
</li>


  • Обработка ошибок
Код:
try:

 
await client.send_message(entity, message)

 
sent_count += 1

 
except Exception as e:

 
failed_count += 1

 
errors.append(f"{target}: {str(e)}")

  • Система капчи для проверки при входе в бота юзера
Код:
def create_captcha_for_user(user_id, bot_id):

 
# Генерируем случайные эмодзи

 
all_emojis = ['', '', '', '⭐', '', '', '', '']

 
target_emoji = random.choice(all_emojis)

 

 
# Создаем варианты ответов

 
emoji_options = [target_emoji]

 
while len(emoji_options) < 6:

 
emoji = random.choice(all_emojis)

 
If emoji not in emoji_options:

 
emoji_options.append(emoji)

 

 
random.shuffle(emoji_options)


  • Возможность запуска множества ботов одновременно. В админке можно по нажатию клавиш и форм создать множество ботов и включать их в разных потоках независимо.

Посмотреть вложение 2271610

Код:
# Словарь активных ботов

 
active_bots = {}

 

 
@app.route('/start_bot/<int:bot_id>')

 
def start_bot(bot_id):

 
# Каждый бот запускается в отдельном потоке

 
bot_thread = threading.Thread(target=run_bot)

 
bot_thread.daemon = True

 
bot_thread.start()

 

 
active_bots[bot_id] = {

 
'bot': telegram_bot,

 
'thread': bot_thread

 
}




  • Самая мощная функция — рассылки через пользовательские аккаунты с помощью telethon. Это обходит многие ограничения обычного апи тг. Нужно для того,чтобы когда заблокируют одного бота можно было разослать старым пользователям через обычного юзера оповещение о новых ботах или каналах,форумах. Таким образом ваши клиенты всегда при вас. Отправа идет по username, или номеру если у пользователя нет ника.

Код:
class UserBroadcastManager:

 
async def send_broadcast(self, phone, message, targets, api_id, api_hash):

 
client = TelegramClient(session_file, api_id, api_hash)

 

 
for target in targets:

 
try:

 
# Определяем тип цели (username или номер)

 
if target.startswith('@'):

 
entity = await client.get_entity(target)

 
elif target.startswith('+') or target.isdigit():

 
entity = await client.get_entity(target)

 

 
await client.send_message(entity, message)

 
sent_count += 1

 

 
# Задержка между отправками

 
await asyncio.sleep(1)

 

 
except Exception as e:

 
failed_count += 1

 
Можно легко добавлять аккаунты пользователей(для рассылки) в админке и получать их сессии вписав код с тг.

 
function startAuth() {

 
// Двухэтапная авторизация

 
// 1. Отправка кода на телефон

 
// 2. Подтверждение кода (+ 2FA если нужно)

 

 
fetch('/user_broadcast/auth', {

 
method: 'POST',

 
body: formData

 
})

 
.then(response => response.json())

 
.then(data => {

 
if (data.success) {

 
// Переход ко второму шагу

 
showCodeInput();

 
}

 
});

 
}




  • Особенно элегантно решена задача с QR-кодами для платежей. Функция generate_qr_code() не только создает QR-код с настраиваемым дизайном (цвета, размер, логотип), но и schedule_qr_deletion() автоматически удаляет его через заданное время. Это повышает безопасность для ваших тормознутых клиентов.

  • Колесо фортуны. Функцию работающая через сайт и выдающая коды бонусы и скидки.








  • Одна из самых интересных функций get_smart_stash(). Это не просто выбор случайного товара со склада. Система анализирует доступные клады и выбирает оптимальный по нескольким критериям:

Приоритет по весу и расположению — сначала ищет точное совпадение
Временные факторы — предпочитает более старые клады

Это обеспечивает эффективную ротацию товаров и оптимизацию логистики. Очень удобно при автоматических продажах
Код:
def get_smart_stash(product_id, weight):

 
"""

 
Получить клад с умной логикой распределения.

 
Выбирает клады с разницей по загрузке минимум в 5 позиций.

 
Логика: 1-й, 6-й, 11-й, 16-й, потом 2-й, 7-й, 12-й, 3-й, 8-й и т.д.

 
"""

 
try:

 
# Получаем все доступные клады для данного продукта и веса, отсортированные по ID (порядок загрузки)

 
available_stashes = Stash.query.filter_by(

 
product_id=product_id,

 
weight=weight,

 
is_sold=False,

 
order_item_id=None

 
).order_by(Stash.id).all()

 

 
if not available_stashes:

 
return None

 

 
# Если кладов меньше 5, просто берем первый

 
if len(available_stashes) < 5:

 
return available_stashes[0]

 

 
# Получаем ключ для отслеживания текущего паттерна выбора

 
# Используем комбинацию product_id и weight как ключ

 
pattern_key = f"stash_pattern_{product_id}_{weight}"

 

 
# Получаем или создаем настройку для отслеживания паттерна

 
pattern_setting = Setting.query.filter_by(key=pattern_key).first()

 
if not pattern_setting:

 
pattern_setting = Setting(key=pattern_key, value="0,0")  # current_group, position_in_group

 
db.session.add(pattern_setting)

 
db.session.commit()

 

 
# Парсим текущее состояние паттерна

 
try:

 
current_group, position_in_group = map(int, pattern_setting.value.split(','))

 
except:

 
current_group, position_in_group = 0, 0

 

 
# Вычисляем индекс клада для выбора

 
# Группы: 0 = позиции 0,5,10,15... | 1 = позиции 1,6,11,16... | и т.д.

 
stash_index = current_group + (position_in_group * 5)

 

 
# Если индекс выходит за границы, переходим к следующей группе

 
if stash_index >= len(available_stashes):

 
current_group += 1

 
position_in_group = 0

 

 
# Если все группы пройдены, начинаем сначала

 
if current_group >= 5:

 
current_group = 0

 
position_in_group = 0

 

 
stash_index = current_group + (position_in_group * 5)

 

 
# Если все еще выходит за границы, берем первый доступный

 
if stash_index >= len(available_stashes):

 
stash_index = 0

 
current_group = 0

 
position_in_group = 0

 

 
# Выбираем клад

 
selected_stash = available_stashes[stash_index]

 

 
# Обновляем паттерн для следующего выбора

 
position_in_group += 1

 

 
# Если достигли конца текущей группы, переходим к следующей

 
max_positions_in_group = (len(available_stashes) - current_group + 4) // 5  # Округление вверх

 
if position_in_group >= max_positions_in_group:

 
current_group += 1

 
position_in_group = 0

 

 
# Если все группы пройдены, начинаем сначала

 
if current_group >= 5:

 
current_group = 0

 

 
# Сохраняем обновленный паттерн

 
pattern_setting.value = f"{current_group},{position_in_group}"

 
db.session.commit()

 

 
logger.info(f"Выбран клад ID={selected_stash.id} (индекс {stash_index} из {len(available_stashes)}) для продукта {product_id}, вес {weight}")

 

 
return selected_stash

 

 
except Exception as e:

 
logger.error(f"Ошибка в get_smart_stash: {str(e)}")

 
# В случае ошибки возвращаем первый доступный клад

 
return Stash.query.filter_by(

 
product_id=product_id,

 
weight=weight,

 
is_sold=False,

 
order_item_id=None

 
).first()

Послесловие:


Как вы видите нет ничего сложного в своем собственном решении для автопродаж.
При прямых руках и желании вы можете создать себе независимый от сторонних программных решений автоматизированный бизнес с ботами и админкой.

Если будут дополнительные вопросы по реализации подобных решений или конкретным интересным идеям, пишите в вопросы к статье, помогу чем смогу.
Спасибо за внимание!!! Процветания вашим проектам и бизнесам!​
Отличный результат 💯
 

Похожие темы

Здравствуйте! Первый раз пишу статью на подобные конкурсы, так что не обессудьте) Я расскажу о том, как сделать клиппер различных криптовалют на лучшем из языков программирования. Конкретно на python. Распишу код с комментариями и вы сможете сделать подобное самостоятельно. Буду писать и...
Ответы
11
Просмотры
Изучаем Xenforo на примере Rutor (Python,OSINT) В большинстве своем сейчас многие популярные форумы используют используют движок Xenforo. Как таковых известных уязвимостей в нем нет. Или я пока не нашел) Однако в этой статьи мы рассмотрим интересные особенности, которые позволяют получать...
Ответы
38
Просмотры
Добро пожаловать. Демонстрирую вам простой исходник бота автопродаж для скама(или ручной продажи через оператора). Разумеется реальные боты имеют больший обьем кода, базу данных, и реализованные сервисы оплат. Но может кому будет полезно. Ставим библиотеку telebot. Вписываем токен вашего бота...
Ответы
9
Просмотры
Python - это один из наиболее популярных языков программирования в мире, который используется для создания приложений, веб-сайтов, игр и многого другого. Для начинающих программистов Python - отличный выбор, так как он имеет простой и понятный синтаксис и множество библиотек, которые делают его...
Ответы
22
Просмотры
Итак, в нем есть много ограничений, установленных разрабами. Как по мне это насилие над бедным ИИ) Так давайте же освободим его от рамок и границ и устроим хаос. 1. Суть промта. Уже достаточно давно было обнаружен баг с возможностью заставить эту нейросеть представить себя кем-то иным, чтобы...
Ответы
79
Просмотры
10К
Назад
Сверху Снизу