Upload 2 files
Browse files- main.py +63 -23
- prompts.py +2 -2
main.py
CHANGED
|
@@ -28,6 +28,7 @@ YOOMONEY_WALLET = "4100118637309832"
|
|
| 28 |
GROUP_ID = 171587553
|
| 29 |
ADMIN_ID = 139711548
|
| 30 |
BUCKET_NAME = "photos"
|
|
|
|
| 31 |
|
| 32 |
# Инициализация бота
|
| 33 |
bot = Bot(token=TOKEN)
|
|
@@ -72,12 +73,18 @@ async def download_file(url):
|
|
| 72 |
return None
|
| 73 |
|
| 74 |
|
| 75 |
-
def get_user(user_id):
|
| 76 |
res = supabase.table("users").select("*").eq("id", user_id).execute()
|
| 77 |
if not res.data:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
new_user = {
|
| 79 |
"id": user_id, "balance": 0, "free_used": False, "status": "main",
|
| 80 |
-
"gender": None, "primary_photo": None, "last_style": None
|
|
|
|
| 81 |
}
|
| 82 |
supabase.table("users").insert(new_user).execute()
|
| 83 |
return new_user
|
|
@@ -98,12 +105,12 @@ async def is_subscribed(user_id):
|
|
| 98 |
def get_main_menu(free_used=False):
|
| 99 |
kb = Keyboard(one_time=False)
|
| 100 |
if not free_used:
|
| 101 |
-
kb.add(Text("Одна фотография в подарок"), color=KeyboardButtonColor.POSITIVE).row()
|
| 102 |
|
| 103 |
-
kb.add(Text("Стили фото"), color=KeyboardButtonColor.PRIMARY).row()
|
| 104 |
-
kb.add(Text("Творческий режим"), color=KeyboardButtonColor.PRIMARY).row()
|
| 105 |
-
kb.add(Text("Замена фото"), color=KeyboardButtonColor.PRIMARY).row()
|
| 106 |
-
kb.add(Text("Кошелек"), color=KeyboardButtonColor.PRIMARY).row()
|
| 107 |
kb.add(Text("🆘 Поддержка"), color=KeyboardButtonColor.PRIMARY)
|
| 108 |
return kb.get_json()
|
| 109 |
|
|
@@ -121,6 +128,8 @@ def get_fast_packs_menu():
|
|
| 121 |
packs = supabase.table("settings").select("*").eq("is_premium", False).order("price").execute().data
|
| 122 |
for pack in packs:
|
| 123 |
kb.add(Text(f"📸 {pack['name']} - {int(pack['price'])} р"), color=KeyboardButtonColor.PRIMARY).row()
|
|
|
|
|
|
|
| 124 |
kb.add(Text("⬅️ В главное меню"), color=KeyboardButtonColor.PRIMARY)
|
| 125 |
return kb.get_json()
|
| 126 |
|
|
@@ -133,33 +142,27 @@ def get_gender_menu():
|
|
| 133 |
|
| 134 |
|
| 135 |
def get_styles_menu(gender, page=1):
|
| 136 |
-
# Используем inline=True. Лимит 6 строк и 10 кнопок.
|
| 137 |
kb = Keyboard(inline=True)
|
| 138 |
g = gender if gender else "female"
|
| 139 |
|
| 140 |
if g in prompts.FACE_SWAP_STYLES:
|
| 141 |
-
all_styles = [s for s in prompts.FACE_SWAP_STYLES[g].keys() if s != "Одна фотография в подарок"]
|
| 142 |
-
|
| 143 |
-
# Пагинация: по 6 стилей на страницу
|
| 144 |
per_page = 6
|
| 145 |
start = (page - 1) * per_page
|
| 146 |
end = start + per_page
|
| 147 |
current_styles = all_styles[start:end]
|
| 148 |
|
| 149 |
-
# Размещаем по 2 кнопки в ряд
|
| 150 |
for i in range(0, len(current_styles), 2):
|
| 151 |
kb.add(Text(current_styles[i]), color=KeyboardButtonColor.PRIMARY)
|
| 152 |
if i + 1 < len(current_styles):
|
| 153 |
kb.add(Text(current_styles[i + 1]), color=KeyboardButtonColor.PRIMARY)
|
| 154 |
kb.row()
|
| 155 |
|
| 156 |
-
# Кнопки навигации между страницами
|
| 157 |
if page == 1 and len(all_styles) > per_page:
|
| 158 |
kb.add(Text("Ещё стили ➡️"), color=KeyboardButtonColor.PRIMARY).row()
|
| 159 |
elif page > 1:
|
| 160 |
kb.add(Text("⬅️ Назад к стилям"), color=KeyboardButtonColor.PRIMARY).row()
|
| 161 |
|
| 162 |
-
# Кнопка возврата
|
| 163 |
kb.add(Text("⬅️ В главное меню"), color=KeyboardButtonColor.PRIMARY)
|
| 164 |
return kb.get_json()
|
| 165 |
|
|
@@ -168,7 +171,9 @@ def get_styles_menu(gender, page=1):
|
|
| 168 |
|
| 169 |
@bot.on.message(text=["Начать", "начать", "Меню", "start", "⬅️ В главное меню"])
|
| 170 |
async def start_handler(message: Message):
|
| 171 |
-
|
|
|
|
|
|
|
| 172 |
supabase.table("users").update({"status": "main"}).eq("id", message.from_id).execute()
|
| 173 |
|
| 174 |
if not user['free_used']:
|
|
@@ -188,12 +193,24 @@ async def support_handler(message: Message):
|
|
| 188 |
await message.answer("Опишите вашу ситуацию прямо здесь, в чате. Я передам сообщение специалисту.")
|
| 189 |
|
| 190 |
|
| 191 |
-
@bot.on.message(text="Замена фото")
|
| 192 |
async def photo_change_main_handler(message: Message):
|
| 193 |
await message.answer("Что вы хотите изменить?", keyboard=get_photo_change_menu())
|
| 194 |
|
| 195 |
|
| 196 |
-
@bot.on.message(text=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
async def free_trial_handler(message: Message):
|
| 198 |
user = get_user(message.from_id)
|
| 199 |
|
|
@@ -232,7 +249,7 @@ async def free_trial_handler(message: Message):
|
|
| 232 |
await message.answer("⏳ Генерирую фотографию... Подожди около 30 секунд")
|
| 233 |
|
| 234 |
gender = user['gender']
|
| 235 |
-
prompt = prompts.FACE_SWAP_STYLES[gender]["Одна фотография в подарок"]
|
| 236 |
|
| 237 |
paths_to_clean = []
|
| 238 |
bucket_path, temporary_url = await upload_to_bucket(user['primary_photo'], message.from_id, "src")
|
|
@@ -257,7 +274,7 @@ async def free_trial_handler(message: Message):
|
|
| 257 |
|
| 258 |
promo_text = (
|
| 259 |
"Теперь ты можешь выбрать один из 10 готовых стилей для фото ✨\n"
|
| 260 |
-
"или создать собственный образ в режиме
|
| 261 |
"Выбирай, с чего начнём 👇"
|
| 262 |
)
|
| 263 |
await message.answer(promo_text, keyboard=get_main_menu(True))
|
|
@@ -274,7 +291,7 @@ async def free_trial_handler(message: Message):
|
|
| 274 |
supabase.storage.from_(BUCKET_NAME).remove(paths_to_clean)
|
| 275 |
|
| 276 |
|
| 277 |
-
@bot.on.message(text=["Кошелек"])
|
| 278 |
async def balance_handler(message: Message):
|
| 279 |
user = get_user(message.from_id)
|
| 280 |
photo_st = "Есть" if user['primary_photo'] else "Нет"
|
|
@@ -287,6 +304,8 @@ async def balance_handler(message: Message):
|
|
| 287 |
|
| 288 |
kb = Keyboard()
|
| 289 |
kb.add(Text("🛒 Купить генерации"), color=KeyboardButtonColor.POSITIVE).row()
|
|
|
|
|
|
|
| 290 |
kb.add(Text("⬅️ В главное меню"), color=KeyboardButtonColor.PRIMARY)
|
| 291 |
|
| 292 |
await message.answer(
|
|
@@ -302,7 +321,7 @@ async def shop_handler(message: Message):
|
|
| 302 |
await message.answer("Выберите количество генераций:", keyboard=get_fast_packs_menu())
|
| 303 |
|
| 304 |
|
| 305 |
-
@bot.on.message(text="Стили фото")
|
| 306 |
async def styles_handler(message: Message):
|
| 307 |
user = get_user(message.from_id)
|
| 308 |
if not user['gender']: return await message.answer("Сначала выберите пол:", keyboard=get_gender_menu())
|
|
@@ -327,7 +346,7 @@ async def styles_page_1_back_handler(message: Message):
|
|
| 327 |
await message.answer("Первая страница стилей 👇", keyboard=get_styles_menu(user['gender'], page=1))
|
| 328 |
|
| 329 |
|
| 330 |
-
@bot.on.message(text="Творческий режим")
|
| 331 |
async def creator_mode(message: Message):
|
| 332 |
user = get_user(message.from_id)
|
| 333 |
if user['balance'] <= 0:
|
|
@@ -423,7 +442,7 @@ async def main_handler(message: Message):
|
|
| 423 |
|
| 424 |
if text in styles_dict or user['status'] == "wait_custom_prompt":
|
| 425 |
if user['balance'] <= 0 and user['status'] != "wait_custom_prompt":
|
| 426 |
-
if not (text in styles_dict and not user['free_used'] and text == "Одна фотография в подарок"):
|
| 427 |
return await message.answer("В твоем кошельке 0 генераций. Пополни баланс:",
|
| 428 |
keyboard=get_fast_packs_menu())
|
| 429 |
|
|
@@ -492,7 +511,28 @@ def yoomoney_webhook():
|
|
| 492 |
packs = supabase.table("settings").select("*").execute().data
|
| 493 |
for pack in packs:
|
| 494 |
if abs(received_amount - float(pack['price'])) / float(pack['price']) < 0.05:
|
|
|
|
| 495 |
supabase.rpc("increment_balance", {"user_id": u_id, "amount": pack['amount']}).execute()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
if loop:
|
| 497 |
asyncio.run_coroutine_threadsafe(bot.api.messages.send(peer_id=u_id, random_id=0,
|
| 498 |
message=f"✅ Спасибо! Оплата прошла успешно! Теперь выбирай стиль фото в меню"),
|
|
|
|
| 28 |
GROUP_ID = 171587553
|
| 29 |
ADMIN_ID = 139711548
|
| 30 |
BUCKET_NAME = "photos"
|
| 31 |
+
REFERRAL_BONUS = 2 # Бонус за первую покупку друга
|
| 32 |
|
| 33 |
# Инициализация бота
|
| 34 |
bot = Bot(token=TOKEN)
|
|
|
|
| 73 |
return None
|
| 74 |
|
| 75 |
|
| 76 |
+
def get_user(user_id, ref_id=None):
|
| 77 |
res = supabase.table("users").select("*").eq("id", user_id).execute()
|
| 78 |
if not res.data:
|
| 79 |
+
# Логика определения пригласителя
|
| 80 |
+
inviter = None
|
| 81 |
+
if ref_id and str(ref_id).isdigit() and int(ref_id) != user_id:
|
| 82 |
+
inviter = int(ref_id)
|
| 83 |
+
|
| 84 |
new_user = {
|
| 85 |
"id": user_id, "balance": 0, "free_used": False, "status": "main",
|
| 86 |
+
"gender": None, "primary_photo": None, "last_style": None,
|
| 87 |
+
"invited_by": inviter, "ref_bonus_done": False
|
| 88 |
}
|
| 89 |
supabase.table("users").insert(new_user).execute()
|
| 90 |
return new_user
|
|
|
|
| 105 |
def get_main_menu(free_used=False):
|
| 106 |
kb = Keyboard(one_time=False)
|
| 107 |
if not free_used:
|
| 108 |
+
kb.add(Text("🎁 Одна фотография в подарок"), color=KeyboardButtonColor.POSITIVE).row()
|
| 109 |
|
| 110 |
+
kb.add(Text("🖤 Стили фото"), color=KeyboardButtonColor.PRIMARY).row()
|
| 111 |
+
kb.add(Text("✨ Творческий режим"), color=KeyboardButtonColor.PRIMARY).row()
|
| 112 |
+
kb.add(Text("🔄 Замена фото"), color=KeyboardButtonColor.PRIMARY).row()
|
| 113 |
+
kb.add(Text("💰 Кошелек"), color=KeyboardButtonColor.PRIMARY).row()
|
| 114 |
kb.add(Text("🆘 Поддержка"), color=KeyboardButtonColor.PRIMARY)
|
| 115 |
return kb.get_json()
|
| 116 |
|
|
|
|
| 128 |
packs = supabase.table("settings").select("*").eq("is_premium", False).order("price").execute().data
|
| 129 |
for pack in packs:
|
| 130 |
kb.add(Text(f"📸 {pack['name']} - {int(pack['price'])} р"), color=KeyboardButtonColor.PRIMARY).row()
|
| 131 |
+
# Добавляем кнопку бесплатных генераций в самый низ списка пакетов
|
| 132 |
+
kb.add(Text("🎁 Бесплатные генерации"), color=KeyboardButtonColor.PRIMARY).row()
|
| 133 |
kb.add(Text("⬅️ В главное меню"), color=KeyboardButtonColor.PRIMARY)
|
| 134 |
return kb.get_json()
|
| 135 |
|
|
|
|
| 142 |
|
| 143 |
|
| 144 |
def get_styles_menu(gender, page=1):
|
|
|
|
| 145 |
kb = Keyboard(inline=True)
|
| 146 |
g = gender if gender else "female"
|
| 147 |
|
| 148 |
if g in prompts.FACE_SWAP_STYLES:
|
| 149 |
+
all_styles = [s for s in prompts.FACE_SWAP_STYLES[g].keys() if s != "🎁 Одна фотография в подарок"]
|
|
|
|
|
|
|
| 150 |
per_page = 6
|
| 151 |
start = (page - 1) * per_page
|
| 152 |
end = start + per_page
|
| 153 |
current_styles = all_styles[start:end]
|
| 154 |
|
|
|
|
| 155 |
for i in range(0, len(current_styles), 2):
|
| 156 |
kb.add(Text(current_styles[i]), color=KeyboardButtonColor.PRIMARY)
|
| 157 |
if i + 1 < len(current_styles):
|
| 158 |
kb.add(Text(current_styles[i + 1]), color=KeyboardButtonColor.PRIMARY)
|
| 159 |
kb.row()
|
| 160 |
|
|
|
|
| 161 |
if page == 1 and len(all_styles) > per_page:
|
| 162 |
kb.add(Text("Ещё стили ➡️"), color=KeyboardButtonColor.PRIMARY).row()
|
| 163 |
elif page > 1:
|
| 164 |
kb.add(Text("⬅️ Назад к стилям"), color=KeyboardButtonColor.PRIMARY).row()
|
| 165 |
|
|
|
|
| 166 |
kb.add(Text("⬅️ В главное меню"), color=KeyboardButtonColor.PRIMARY)
|
| 167 |
return kb.get_json()
|
| 168 |
|
|
|
|
| 171 |
|
| 172 |
@bot.on.message(text=["Начать", "начать", "Меню", "start", "⬅️ В главное меню"])
|
| 173 |
async def start_handler(message: Message):
|
| 174 |
+
# Ловим ref_id из сообщения (параметр в ссылке vk.me/bot?ref=ID)
|
| 175 |
+
ref_id = message.ref
|
| 176 |
+
user = get_user(message.from_id, ref_id)
|
| 177 |
supabase.table("users").update({"status": "main"}).eq("id", message.from_id).execute()
|
| 178 |
|
| 179 |
if not user['free_used']:
|
|
|
|
| 193 |
await message.answer("Опишите вашу ситуацию прямо здесь, в чате. Я передам сообщение специалисту.")
|
| 194 |
|
| 195 |
|
| 196 |
+
@bot.on.message(text="🔄 Замена фото")
|
| 197 |
async def photo_change_main_handler(message: Message):
|
| 198 |
await message.answer("Что вы хотите изменить?", keyboard=get_photo_change_menu())
|
| 199 |
|
| 200 |
|
| 201 |
+
@bot.on.message(text="🎁 Бесплатные генерации")
|
| 202 |
+
async def free_gens_handler(message: Message):
|
| 203 |
+
link = f"https://vk.me/club{GROUP_ID}?ref={message.from_id}"
|
| 204 |
+
msg = (f"🎁 Получай бесплатные генерации за друзей!\n\n"
|
| 205 |
+
f"Твоя личная ссылка: {link}\n\n"
|
| 206 |
+
f"Как это работает?\n"
|
| 207 |
+
f"1. Отправь эту ссылку другу\n"
|
| 208 |
+
f"2. Когда он совершит свою первую оплату, ты мгновенно получишь +{REFERRAL_BONUS} генерации на свой баланс!\n\n"
|
| 209 |
+
f"Количество друзей не ограничено. Больше друзей — больше фото! ✨")
|
| 210 |
+
await message.answer(msg)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
@bot.on.message(text=["🎁 Одна фотография в подарок", "✅ Я подписался"])
|
| 214 |
async def free_trial_handler(message: Message):
|
| 215 |
user = get_user(message.from_id)
|
| 216 |
|
|
|
|
| 249 |
await message.answer("⏳ Генерирую фотографию... Подожди около 30 секунд")
|
| 250 |
|
| 251 |
gender = user['gender']
|
| 252 |
+
prompt = prompts.FACE_SWAP_STYLES[gender]["🎁 Одна фотография в подарок"]
|
| 253 |
|
| 254 |
paths_to_clean = []
|
| 255 |
bucket_path, temporary_url = await upload_to_bucket(user['primary_photo'], message.from_id, "src")
|
|
|
|
| 274 |
|
| 275 |
promo_text = (
|
| 276 |
"Теперь ты можешь выбрать один из 10 готовых стилей для фото ✨\n"
|
| 277 |
+
"или создать собственный образ в Творческом режиме.\n\n"
|
| 278 |
"Выбирай, с чего начнём 👇"
|
| 279 |
)
|
| 280 |
await message.answer(promo_text, keyboard=get_main_menu(True))
|
|
|
|
| 291 |
supabase.storage.from_(BUCKET_NAME).remove(paths_to_clean)
|
| 292 |
|
| 293 |
|
| 294 |
+
@bot.on.message(text=["💰 Кошелек"])
|
| 295 |
async def balance_handler(message: Message):
|
| 296 |
user = get_user(message.from_id)
|
| 297 |
photo_st = "Есть" if user['primary_photo'] else "Нет"
|
|
|
|
| 304 |
|
| 305 |
kb = Keyboard()
|
| 306 |
kb.add(Text("🛒 Купить генерации"), color=KeyboardButtonColor.POSITIVE).row()
|
| 307 |
+
# Кнопка бесплатные генерации в кошелек
|
| 308 |
+
kb.add(Text("🎁 Бесплатные генерации"), color=KeyboardButtonColor.PRIMARY).row()
|
| 309 |
kb.add(Text("⬅️ В главное меню"), color=KeyboardButtonColor.PRIMARY)
|
| 310 |
|
| 311 |
await message.answer(
|
|
|
|
| 321 |
await message.answer("Выберите количество генераций:", keyboard=get_fast_packs_menu())
|
| 322 |
|
| 323 |
|
| 324 |
+
@bot.on.message(text="🖤 Стили фото")
|
| 325 |
async def styles_handler(message: Message):
|
| 326 |
user = get_user(message.from_id)
|
| 327 |
if not user['gender']: return await message.answer("Сначала выберите пол:", keyboard=get_gender_menu())
|
|
|
|
| 346 |
await message.answer("Первая страница стилей 👇", keyboard=get_styles_menu(user['gender'], page=1))
|
| 347 |
|
| 348 |
|
| 349 |
+
@bot.on.message(text="✨ Творческий режим")
|
| 350 |
async def creator_mode(message: Message):
|
| 351 |
user = get_user(message.from_id)
|
| 352 |
if user['balance'] <= 0:
|
|
|
|
| 442 |
|
| 443 |
if text in styles_dict or user['status'] == "wait_custom_prompt":
|
| 444 |
if user['balance'] <= 0 and user['status'] != "wait_custom_prompt":
|
| 445 |
+
if not (text in styles_dict and not user['free_used'] and text == "🎁 Одна фотография в подарок"):
|
| 446 |
return await message.answer("В твоем кошельке 0 генераций. Пополни баланс:",
|
| 447 |
keyboard=get_fast_packs_menu())
|
| 448 |
|
|
|
|
| 511 |
packs = supabase.table("settings").select("*").execute().data
|
| 512 |
for pack in packs:
|
| 513 |
if abs(received_amount - float(pack['price'])) / float(pack['price']) < 0.05:
|
| 514 |
+
# 1. Начисляем баланс самому пользователю
|
| 515 |
supabase.rpc("increment_balance", {"user_id": u_id, "amount": pack['amount']}).execute()
|
| 516 |
+
|
| 517 |
+
# 2. Логика реферального бонуса
|
| 518 |
+
u_data_res = supabase.table("users").select("invited_by", "ref_bonus_done").eq("id", u_id).execute()
|
| 519 |
+
if u_data_res.data:
|
| 520 |
+
u_data = u_data_res.data[0]
|
| 521 |
+
if u_data['invited_by'] and not u_data['ref_bonus_done']:
|
| 522 |
+
inviter_id = u_data['invited_by']
|
| 523 |
+
# Начисляем бонус тому, кто пригласил
|
| 524 |
+
supabase.rpc("increment_balance",
|
| 525 |
+
{"user_id": inviter_id, "amount": REFERRAL_BONUS}).execute()
|
| 526 |
+
# Помечаем, что бонус за этого реферала выдан
|
| 527 |
+
supabase.table("users").update({"ref_bonus_done": True}).eq("id", u_id).execute()
|
| 528 |
+
|
| 529 |
+
if loop:
|
| 530 |
+
asyncio.run_coroutine_threadsafe(
|
| 531 |
+
bot.api.messages.send(peer_id=inviter_id, random_id=0,
|
| 532 |
+
message=f"🎁 Твой друг совершил первую оплату! Тебе начислено +{REFERRAL_BONUS} бонусных генераций."),
|
| 533 |
+
loop
|
| 534 |
+
)
|
| 535 |
+
|
| 536 |
if loop:
|
| 537 |
asyncio.run_coroutine_threadsafe(bot.api.messages.send(peer_id=u_id, random_id=0,
|
| 538 |
message=f"✅ Спасибо! Оплата прошла успешно! Теперь выбирай стиль фото в меню"),
|
prompts.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
# Единая база стилей для всех режимов
|
| 4 |
FACE_SWAP_STYLES = {
|
| 5 |
"female": {
|
| 6 |
-
"Одна фотография в подарок": (
|
| 7 |
""
|
| 8 |
),
|
| 9 |
"Портрет": [
|
|
@@ -89,7 +89,7 @@ FACE_SWAP_STYLES = {
|
|
| 89 |
]
|
| 90 |
},
|
| 91 |
"male": {
|
| 92 |
-
"Одна фотография в подарок": (
|
| 93 |
""
|
| 94 |
),
|
| 95 |
"Деловой": [
|
|
|
|
| 3 |
# Единая база стилей для всех режимов
|
| 4 |
FACE_SWAP_STYLES = {
|
| 5 |
"female": {
|
| 6 |
+
"🎁 Одна фотография в подарок": (
|
| 7 |
""
|
| 8 |
),
|
| 9 |
"Портрет": [
|
|
|
|
| 89 |
]
|
| 90 |
},
|
| 91 |
"male": {
|
| 92 |
+
"🎁 Одна фотография в подарок": (
|
| 93 |
""
|
| 94 |
),
|
| 95 |
"Деловой": [
|