diff --git a/README.md b/README.md deleted file mode 100644 index 82e78f26..00000000 --- a/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# 🚀 SoloBot - -**[SoloBot]**! -[Бот для API x-ray] - -## 📋 Оглавление -1. [Описание](#описание) -2. [Требования](#требования) -3. [Установка](#установка) -4. [Конфигурация](#конфигурация) -5. [Запуск](#запуск) -6. [Автор](#автор) - ---- - -## 📖 Описание - -[Бот предназначен для автоматизирования выдачи ключей, хранении их в базе данных и управления подключениями] - - ---- - -## 💻 Требования - -Для работы проекта необходимы следующие компоненты: - -- **Python** версии 3.8 или выше -- **Git** для клонирования репозитория -- **Virtualenv** для создания виртуального окружения (рекомендуется) -- **postgresql** для работы базы данных - ---- - -## ⚙️ Установка - -### 1️⃣ Шаг 1: Клонирование репозитория - -Клонируйте репозиторий и перейдите в его директорию: - -```bash -git clone https://github.com/Vladless/Solo_bot.git -cd solo_bot -``` - -### 2️⃣ Шаг 2: Создание и активация виртуального окружения - -``` -python3 -m venv venv -source venv/bin/activate -``` - -### 3️⃣ Шаг 3: Установка зависимостей - -``` -pip install -r requirements.txt -``` - -### 🛠️ Конфигурация - -Для правильной работы вам нужно установить и запустить postgresql, создать пользователя для работы с базой данных и выдать ему права - -Также создайте файл config.py в корневой папке проекта с вашими данными: - -``` -API_TOKEN = токен вашего бота телеграм -API_URL = домен вашего x-ray сервера с указанием порта и дополнительным путем панели вида "https://{domen}:{port}/{путь}" без слэша в конце -ADMIN_USERNAME = логин от вашей панели x-ray -ADMIN_PASSWORD = пароль от вашей панели x-ray -ADD_CLIENT_URL = f"{API_URL}/panel/api/inbounds/addClient" -GET_INBOUNDS_URL = f"{API_URL}/panel/api/inbounds/list/" -AUTH_URL = f"{API_URL}/login/" -DOMEN = "{ваш домен без https://}:443" -DATABASE_URL = путь к вашей базе данных, имеет вид "postgresql://{user}:{password}@{Ip-adress}:{port}/{имя базы данных}" -ADMIN_ID = ID телеграм профиля администратора -CHANNEL_URL = ссылка на ваш телеграм канал -``` - -### 🚀 Запуск - -введите команду из виртуального окружения - -``` -python main.py -``` -🔗 SoloBot в Telegram - -Попробуйте SoloBot прямо сейчас в Telegram [по этой ссылке](https://t.me/SoloNetVPN_bot). \ No newline at end of file diff --git a/handlers/key_management.py b/handlers/key_management.py index ea3f8dc8..33c36904 100644 --- a/handlers/key_management.py +++ b/handlers/key_management.py @@ -1,6 +1,7 @@ import re import uuid from datetime import datetime, timedelta +from aiogram.filters import Command import asyncpg from aiogram import F, Router, types @@ -10,12 +11,11 @@ from aiogram.types import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message) from auth import link, login_with_credentials -from bot import dp +from bot import dp, bot from client import add_client -from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS +from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS, ADMIN_ID from database import add_connection, get_balance, store_key, update_balance from handlers.instructions import send_instructions -from handlers.notifications import send_message_to_all_clients from handlers.profile import process_callback_view_profile from handlers.start import start_command @@ -28,6 +28,7 @@ class Form(StatesGroup): waiting_for_server_selection = State() waiting_for_key_name = State() viewing_profile = State() + waiting_for_message = State() @dp.callback_query(F.data == 'create_key') async def process_callback_create_key(callback_query: CallbackQuery, state: FSMContext): @@ -122,26 +123,75 @@ async def cancel_create_key(callback_query: CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state) await callback_query.answer() -@dp.message() -async def handle_text(message: Message, state: FSMContext): +@router.message(Command('start')) +async def handle_start(message: types.Message, state: FSMContext): + # Обработка команды /start + await start_command(message) + +@router.message(Command('menu')) +async def handle_menu(message: types.Message, state: FSMContext): + # Обработка команды /menu + await start_command(message) + +@router.message(Command('send_to_all')) +async def send_message_to_all_clients(message: types.Message, state: FSMContext): + if message.from_user.id != ADMIN_ID: + await message.answer("У вас нет прав для выполнения этой команды.") + return + + await message.answer("Введите текст сообщения, который вы хотите отправить всем клиентам:") + await state.set_state(Form.waiting_for_message) # Устанавливаем состояние ожидания сообщения + +@router.message(Form.waiting_for_message) +async def process_message_to_all(message: types.Message, state: FSMContext): + text_message = message.text # Получаем текст сообщения + + try: + conn = await asyncpg.connect(DATABASE_URL) + tg_ids = await conn.fetch('SELECT tg_id FROM connections') + + for record in tg_ids: + tg_id = record['tg_id'] + try: + await bot.send_message(chat_id=tg_id, text=text_message) + except Exception as e: + print(f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя.") + + await message.answer("Сообщение было отправлено всем клиентам.") + except Exception as e: + print(f"Ошибка при подключении к базе данных: {e}") + await message.answer("Произошла ошибка при отправке сообщения.") + finally: + await conn.close() + + await state.clear() # Очистка состояния после отправки сообщения + +@router.message() +async def handle_text(message: types.Message, state: FSMContext): current_state = await state.get_state() - # Обработка команд - if message.text in ["/start", "/menu"]: - await start_command(message) + if message.text in ["/send_to_all"]: + await send_message_to_all_clients(message, state) # Передаем состояние return if message.text == "Мой профиль": - await process_callback_view_profile(message) + callback_query = types.CallbackQuery( + id="1", + from_user=message.from_user, + chat_instance='', + data='view_profile', + message=message + ) + await process_callback_view_profile(callback_query, state) return - # Проверка текущего состояния if current_state == Form.waiting_for_key_name.state: await handle_key_name_input(message, state) - else: + elif current_state is None: # Если состояние не задано, обрабатываем обычные сообщения await start_command(message) + async def handle_key_name_input(message: Message, state: FSMContext): tg_id = message.from_user.id key_name = sanitize_key_name(message.text) diff --git a/handlers/notifications.py b/handlers/notifications.py index 530d1b73..1229dd7e 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -96,35 +96,3 @@ async def notify_expiring_keys(bot: Bot): await conn.close() except Exception as e: print(f"Ошибка при отправке уведомлений: {e}") - - -@router.message(Command('send_to_all')) -async def send_message_to_all_clients(message: types.Message): - if message.from_user.id != ADMIN_ID: - await message.answer("У вас нет прав для выполнения этой команды.") - return - - text = message.text.split(maxsplit=1) - if len(text) < 2: - await message.answer("Пожалуйста, введите текст сообщения после команды.") - return - - text_message = text[1] - - try: - conn = await asyncpg.connect(DATABASE_URL) - tg_ids = await conn.fetch('SELECT tg_id FROM connections') - - for record in tg_ids: - tg_id = record['tg_id'] - try: - await bot.send_message(chat_id=tg_id, text=text_message) - except Exception as e: - print(f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя.") - - await message.answer("Сообщение было отправлено всем клиентам.") - except Exception as e: - print(f"Ошибка при подключении к базе данных: {e}") - await message.answer("Произошла ошибка при отправке сообщения.") - finally: - await conn.close() \ No newline at end of file