TV/Send_message/custom_refferal_level/buttons
This commit is contained in:
+14
-13
@@ -594,14 +594,11 @@ async def handle_referral_on_balance_update(tg_id: int, amount: float):
|
||||
Обработка многоуровневой реферальной системы при обновлении баланса пользователя.
|
||||
|
||||
Метод анализирует цепочку рефералов для указанного пользователя и начисляет
|
||||
бонусы рефереерам на разных уровнях согласно настроенным процентам.
|
||||
бонусы реферерам на разных уровнях согласно настроенным процентам.
|
||||
|
||||
Args:
|
||||
tg_id (int): Идентификатор Telegram пользователя, пополнившего баланс
|
||||
amount (float): Сумма пополнения баланса
|
||||
|
||||
Raises:
|
||||
Exception: В случае ошибки при работе с базой данных
|
||||
"""
|
||||
conn = None
|
||||
try:
|
||||
@@ -609,8 +606,11 @@ async def handle_referral_on_balance_update(tg_id: int, amount: float):
|
||||
logger.info(f"Начало обработки реферальной системы для пользователя {tg_id}")
|
||||
|
||||
MAX_REFERRAL_LEVELS = len(REFERRAL_BONUS_PERCENTAGES.keys())
|
||||
visited_tg_ids = set()
|
||||
if MAX_REFERRAL_LEVELS == 0:
|
||||
logger.warning("Реферальные бонусы отключены.")
|
||||
return
|
||||
|
||||
visited_tg_ids = set()
|
||||
current_tg_id = tg_id
|
||||
referral_chain = []
|
||||
|
||||
@@ -695,8 +695,10 @@ async def get_referral_stats(referrer_tg_id: int):
|
||||
)
|
||||
logger.debug(f"Получено количество активных рефералов: {active_referrals}")
|
||||
|
||||
MAX_REFERRAL_LEVELS = len(REFERRAL_BONUS_PERCENTAGES.keys())
|
||||
|
||||
referrals_by_level_records = await conn.fetch(
|
||||
"""
|
||||
f"""
|
||||
WITH RECURSIVE referral_levels AS (
|
||||
SELECT referred_tg_id, referrer_tg_id, 1 AS level
|
||||
FROM referrals
|
||||
@@ -707,7 +709,7 @@ async def get_referral_stats(referrer_tg_id: int):
|
||||
SELECT r.referred_tg_id, r.referrer_tg_id, rl.level + 1
|
||||
FROM referrals r
|
||||
JOIN referral_levels rl ON r.referrer_tg_id = rl.referred_tg_id
|
||||
WHERE rl.level < 5
|
||||
WHERE rl.level < {MAX_REFERRAL_LEVELS}
|
||||
)
|
||||
SELECT level,
|
||||
COUNT(*) AS level_count,
|
||||
@@ -730,7 +732,7 @@ async def get_referral_stats(referrer_tg_id: int):
|
||||
logger.debug(f"Получена статистика рефералов по уровням: {referrals_by_level}")
|
||||
|
||||
total_referral_bonus = await conn.fetchval(
|
||||
"""
|
||||
f"""
|
||||
WITH RECURSIVE referral_levels AS (
|
||||
SELECT
|
||||
referred_tg_id,
|
||||
@@ -747,13 +749,11 @@ async def get_referral_stats(referrer_tg_id: int):
|
||||
rl.level + 1
|
||||
FROM referrals r
|
||||
JOIN referral_levels rl ON r.referrer_tg_id = rl.referred_tg_id
|
||||
WHERE rl.level < 5
|
||||
WHERE rl.level < {MAX_REFERRAL_LEVELS}
|
||||
)
|
||||
SELECT
|
||||
SUM(p.amount * CASE
|
||||
WHEN rl.level = 1 THEN 0.25
|
||||
WHEN rl.level = 2 THEN 0.10
|
||||
WHEN rl.level = 3 THEN 0.05
|
||||
{" ".join([f"WHEN rl.level = {level} THEN {REFERRAL_BONUS_PERCENTAGES[level]}" for level in REFERRAL_BONUS_PERCENTAGES])}
|
||||
ELSE 0
|
||||
END) AS total_bonus
|
||||
FROM referral_levels rl
|
||||
@@ -762,6 +762,8 @@ async def get_referral_stats(referrer_tg_id: int):
|
||||
""",
|
||||
referrer_tg_id,
|
||||
)
|
||||
|
||||
total_referral_bonus = total_referral_bonus or 0
|
||||
logger.debug(
|
||||
f"Получена общая сумма бонусов от рефералов: {total_referral_bonus}"
|
||||
)
|
||||
@@ -784,7 +786,6 @@ async def get_referral_stats(referrer_tg_id: int):
|
||||
logger.info("Закрытие подключения к базе данных")
|
||||
|
||||
|
||||
|
||||
async def update_key_expiry(client_id: str, new_expiry_time: int):
|
||||
"""
|
||||
Обновление времени истечения ключа для указанного клиента.
|
||||
|
||||
@@ -34,6 +34,8 @@ async def handle_admin_callback_query(callback_query: CallbackQuery, state: FSMC
|
||||
async def handle_admin_message(message: types.Message, state: FSMContext):
|
||||
await state.clear()
|
||||
|
||||
BOT_VERSION = "3.2.3-beta" # Укажите текущую версию бота
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
@@ -58,14 +60,37 @@ async def handle_admin_message(message: types.Message, state: FSMContext):
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="📢 Массовая рассылка", callback_data="send_to_alls")
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="🤖 Управление Ботом", callback_data="bot_management"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")
|
||||
)
|
||||
await message.answer(
|
||||
f"🤖 Панель администратора\n\nВерсия бота: <b>{BOT_VERSION}</b>",
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "bot_management")
|
||||
async def handle_bot_management(callback_query: types.CallbackQuery):
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="💾 Создать резервную копию", callback_data="backups")
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="🔄 Перезагрузить бота", callback_data="restart_bot")
|
||||
)
|
||||
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
|
||||
await message.answer("🤖 Панель администратора", reply_markup=builder.as_markup())
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="⬅️ Назад", callback_data="admin")
|
||||
)
|
||||
await callback_query.message.answer(
|
||||
"🤖 Управление ботом",
|
||||
reply_markup=builder.as_markup(),
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "user_stats", IsAdminFilter())
|
||||
@@ -163,7 +188,7 @@ async def export_users_csv(callback_query: CallbackQuery, session: Any):
|
||||
)
|
||||
return
|
||||
|
||||
csv_data = "tg_id,username,first_name,last_name,language_code,is_bot,balance,trial\n" # Заголовки CSV
|
||||
csv_data = "tg_id,username,first_name,last_name,language_code,is_bot,balance,trial\n"
|
||||
for user in users:
|
||||
csv_data += f"{user['tg_id']},{user['username']},{user['first_name']},{user['last_name']},{user['language_code']},{user['is_bot']},{user['balance']},{user['trial']}\n"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from aiogram import F, Router, types
|
||||
from aiogram import Bot, F, Router, types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
|
||||
@@ -29,6 +29,7 @@ class UserEditorState(StatesGroup):
|
||||
waiting_for_new_balance = State()
|
||||
waiting_for_key_name = State()
|
||||
waiting_for_expiry_time = State()
|
||||
waiting_for_message_text = State()
|
||||
|
||||
|
||||
@router.callback_query(F.data == "search_by_tg_id", IsAdminFilter())
|
||||
@@ -114,6 +115,12 @@ async def handle_username_input(
|
||||
)
|
||||
)
|
||||
builder.row(InlineKeyboardButton(text="❌ Удалить клиента", callback_data=f"confirm_delete_user_{tg_id}"))
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="✉️ Отправить сообщение",
|
||||
callback_data=f"send_message_{tg_id}"
|
||||
)
|
||||
)
|
||||
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor"))
|
||||
|
||||
user_info = (
|
||||
@@ -128,6 +135,35 @@ async def handle_username_input(
|
||||
await state.set_state(UserEditorState.displaying_user_info)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("send_message_"))
|
||||
async def handle_send_message(callback_query: types.CallbackQuery, state: FSMContext):
|
||||
tg_id = callback_query.data.split("_")[2]
|
||||
await state.update_data(target_tg_id=tg_id)
|
||||
await callback_query.message.answer(
|
||||
"✉️ Введите текст сообщения, которое вы хотите отправить пользователю."
|
||||
)
|
||||
await state.set_state(UserEditorState.waiting_for_message_text)
|
||||
|
||||
|
||||
@router.message(UserEditorState.waiting_for_message_text, IsAdminFilter())
|
||||
async def process_send_message(message: types.Message, state: FSMContext, bot: Bot):
|
||||
data = await state.get_data()
|
||||
target_tg_id = data.get("target_tg_id")
|
||||
|
||||
if not target_tg_id:
|
||||
await message.answer("🚫 Ошибка: ID пользователя не найден.")
|
||||
await state.clear()
|
||||
return
|
||||
|
||||
try:
|
||||
await bot.send_message(chat_id=target_tg_id, text=message.text)
|
||||
await message.answer("✅ Сообщение успешно отправлено.")
|
||||
except Exception as e:
|
||||
await message.answer(f"❌ Не удалось отправить сообщение: {e}")
|
||||
|
||||
await state.clear()
|
||||
|
||||
|
||||
@router.message(UserEditorState.waiting_for_tg_id, F.text.isdigit(), IsAdminFilter())
|
||||
async def handle_tg_id_input(message: types.Message, state: FSMContext, session: Any):
|
||||
tg_id = int(message.text)
|
||||
@@ -172,6 +208,12 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext, session:
|
||||
callback_data=f"restore_trial_{tg_id}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="✉️ Отправить сообщение",
|
||||
callback_data=f"send_message_{tg_id}"
|
||||
)
|
||||
)
|
||||
|
||||
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor"))
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ GIFT = "🎁 Подарить подписку"
|
||||
MY_GIFTS = "🎁 Мои подарки"
|
||||
PROFILE = "👤 Личный кабинет"
|
||||
BACK = "🔙 Назад"
|
||||
GIFTS_ABOUT = "<b>Дарите подарки и следите, чтобы они дошли до адресата! 🎄</b>"
|
||||
GIFTS_ABOUT = "<b>Дарите подарки и следите, чтобы они дошли до адресата! 🎄</b>"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ADD_SUB = "➕ Устройство"
|
||||
MY_SUBS = "📱 Мои устройства"
|
||||
PAYMENT = "💳 Пополнить баланс"
|
||||
INVITE = "👥 Пригласить"
|
||||
GIFTS = "🎁 Подарить"
|
||||
TARRIFS = "💡 Тарифы"
|
||||
INSTRUCTIONS = "📘 Инструкции"
|
||||
MAIN_MENU = "⬅️ Главное меню"
|
||||
@@ -1,3 +1,3 @@
|
||||
PAY = "Пополнить"
|
||||
BACK = "⬅️ Назад"
|
||||
CUSTOM_SUM = "💰 Ввести свою сумму"
|
||||
CUSTOM_SUM = "💰 Ввести свою сумму"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import asyncpg
|
||||
from aiogram import F, Router, types
|
||||
from aiogram.types import BufferedInputFile, InlineKeyboardButton
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from config import CONNECT_MACOS, CONNECT_WINDOWS, SUPPORT_CHAT_URL
|
||||
from handlers.texts import INSTRUCTION_PC, INSTRUCTIONS, KEY_MESSAGE
|
||||
from config import CONNECT_MACOS, CONNECT_WINDOWS, DATABASE_URL, SUPPORT_CHAT_URL
|
||||
from handlers.texts import CONNECT_TV_TEXT, INSTRUCTION_PC, INSTRUCTIONS, KEY_MESSAGE, SUBSCRIPTION_DETAILS_TEXT
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -87,3 +88,69 @@ async def process_connect_pc(callback_query: types.CallbackQuery, session: Any):
|
||||
parse_mode="HTML",
|
||||
disable_web_page_preview=True,
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("connect_tv|"))
|
||||
async def process_connect_tv(callback_query: types.CallbackQuery):
|
||||
key_name = callback_query.data.split("|")[1]
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="▶ Продолжить", callback_data=f"continue_tv|{key_name}"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")
|
||||
)
|
||||
|
||||
await callback_query.message.answer(
|
||||
text=CONNECT_TV_TEXT,
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("continue_tv|"))
|
||||
async def process_continue_tv(callback_query: types.CallbackQuery):
|
||||
key_name = callback_query.data.split("|")[1]
|
||||
tg_id = callback_query.from_user.id
|
||||
|
||||
print(f"tg_id: {tg_id}, key_name: {key_name}") # Отладочный вывод
|
||||
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
try:
|
||||
record = await conn.fetchrow(
|
||||
"""
|
||||
SELECT k.key
|
||||
FROM keys k
|
||||
WHERE k.tg_id = $1 AND k.email = $2
|
||||
""",
|
||||
tg_id,
|
||||
key_name,
|
||||
)
|
||||
|
||||
print(f"Query result: {record}") # Отладочный вывод результата
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
|
||||
subscription_link = record['key']
|
||||
|
||||
message_text = SUBSCRIPTION_DETAILS_TEXT.format(subscription_link=subscription_link)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="📖 Полная инструкция", url="https://vpn4tv.com/quick-guide.html"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")
|
||||
)
|
||||
|
||||
await callback_query.message.answer(
|
||||
text=message_text,
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
@@ -208,7 +208,10 @@ async def process_callback_view_key(callback_query: types.CallbackQuery, session
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="💻 Windows/Linux", callback_data=f"connect_pc|{key_name}"
|
||||
text="💻 Компьютеры", callback_data=f"connect_pc|{key_name}"
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text="📺 Андроид TV", callback_data=f"connect_tv|{key_name}"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+821
-771
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+2295
-1478
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+9
-8
@@ -7,6 +7,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from config import NEWS_MESSAGE, RENEWAL_PLANS
|
||||
from database import get_balance, get_key_count, get_referral_stats
|
||||
from handlers.buttons.profile import ADD_SUB, GIFTS, INSTRUCTIONS, INVITE, MAIN_MENU, MY_SUBS, PAYMENT, TARRIFS
|
||||
from handlers.texts import get_referral_link, invite_message_send, profile_message_send
|
||||
|
||||
router = Router()
|
||||
@@ -41,29 +42,29 @@ async def process_callback_view_profile(
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="➕ Устройство", callback_data="create_key"),
|
||||
InlineKeyboardButton(text="📱 Мои устройства", callback_data="view_keys"),
|
||||
InlineKeyboardButton(text=ADD_SUB, callback_data="create_key"),
|
||||
InlineKeyboardButton(text=MY_SUBS, callback_data="view_keys"),
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="💳 Пополнить баланс",
|
||||
text=PAYMENT,
|
||||
callback_data="pay",
|
||||
)
|
||||
)
|
||||
builder.row()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="👥 Пригласить", callback_data="invite"),
|
||||
InlineKeyboardButton(text="🎁 Подарить", callback_data="gifts"),
|
||||
InlineKeyboardButton(text=INVITE, callback_data="invite"),
|
||||
InlineKeyboardButton(text=GIFTS, callback_data="gifts"),
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="💡 Тарифы", callback_data="view_tariffs"),
|
||||
InlineKeyboardButton(text="📘 Инструкции", callback_data="instructions"),
|
||||
InlineKeyboardButton(text=TARRIFS, callback_data="view_tariffs"),
|
||||
InlineKeyboardButton(text=INSTRUCTIONS, callback_data="instructions"),
|
||||
)
|
||||
if admin:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="🔧 Администратор", callback_data="admin")
|
||||
)
|
||||
builder.row(InlineKeyboardButton(text="⬅️ Главное меню", callback_data="start"))
|
||||
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="start"))
|
||||
|
||||
if os.path.isfile(image_path):
|
||||
with open(image_path, "rb") as image_file:
|
||||
|
||||
Reference in New Issue
Block a user