Rid3 commited on
Commit
61babb1
·
verified ·
1 Parent(s): a129af8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +288 -375
app.py CHANGED
@@ -1,426 +1,339 @@
 
1
  import os
2
  import asyncio
3
- import time
4
- import re
5
- import urllib.parse
6
- import io
7
  import logging
8
- import random
9
- import json
 
 
 
 
 
 
 
10
  import aiohttp
11
- from typing import Optional, Dict
12
 
13
- from aiohttp import web
14
- from telegram import (
15
- Update,
16
- InlineKeyboardButton,
17
- InlineKeyboardMarkup,
18
- )
19
- from telegram.ext import (
20
- Application,
21
- CommandHandler,
22
- MessageHandler,
23
- CallbackQueryHandler,
24
- ContextTypes,
25
- filters,
26
- )
27
- from telegram.constants import ParseMode, ChatAction
28
-
29
- # ============================================================
30
- # НАСТРОЙКА ЛОГИРОВАНИЯ
31
- # ============================================================
32
- logging.basicConfig(
33
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
34
- level=logging.INFO,
35
- )
36
  logger = logging.getLogger(__name__)
37
 
38
- # ============================================================
39
- # КОНФИГУРАЦИЯ БОТА И КЛЮЧЕЙ
40
- # ============================================================
41
- BOT_TOKEN = os.environ.get("BOT_TOKEN", "8605889873:AAE2gV2t0psXKlj-8h9ksyyoCrWa8Q-TdkA")
42
- CF_WORKER_URL = os.environ.get("CF_WORKER_URL", "https://tg-proxy.artyomanisimov37.workers.dev")
43
- WEBHOOK_URL = os.environ.get("WEBHOOK_URL", "https://rid3-aibottbh.hf.space")
44
- PORT = int(os.environ.get("PORT", 7860))
45
-
46
- # Ключи для ежечасных объявлений
47
- ANNOUNCE_KEYS = [
48
  "AIzaSyC59FksQAoeVeBKzVpxpL8zByEg3DAkKgE",
49
  "AIzaSyDCL7CImQjZ3DlInbjmWDWw7UeQ5bQDUbU"
50
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- # Ключи для общения с вайтлистом
53
- CHAT_KEYS = [
54
- "AIzaSyCuJfvES31KE86l0NM-rXUnbTIoooh8kis",
55
- "AIzaSyA2vr6mbAEcBMNL1LM36-PjA2_ZVfpHia0",
56
- "AIzaSyCXkFnlqbv_J3g2JIu3agxRM3Rh-g-Y_Tc",
57
- "AIzaSyBp47tYRwstD2givN1ptPAmwytZMTDp7-4",
58
- "AIzaSyDDbqYYtHXPmVq7l0htd9RXavuV0puqr5o",
59
- "AIzaSyC_Rq4JfCD30sUb_Df09LVR6CtfirGu9P4"
60
- ]
61
-
62
- ADMIN_PASSWORD = "artyom2010Q"
63
-
64
- # Лимит запросов на один ключ до того, как он "устанет" (напр. 20 запросов)
65
- KEY_MAX_USES = 20
66
- KEY_COOLDOWN_SECONDS = 3600 # Час отдыха
67
-
68
- # ============================================================
69
- # БАЗА ДАННЫХ (Бот запоминает админов, вайтлист и чаты)
70
- # ============================================================
71
- DB_FILE = "bot_db.json"
72
-
73
- def load_db() -> dict:
74
- if os.path.exists(DB_FILE):
75
- try:
76
- with open(DB_FILE, "r", encoding="utf-8") as f:
77
- return json.load(f)
78
- except Exception as e:
79
- logger.error(f"Ошибка чтения БД: {e}")
80
- # Дефолтная структура
81
- return {
82
- "admins": [],
83
- "whitelist": [],
84
- "known_chats": [],
85
- "key_usage": {}
86
- }
87
-
88
- def save_db(data: dict):
89
- with open(DB_FILE, "w", encoding="utf-8") as f:
90
- json.dump(data, f, ensure_ascii=False, indent=2)
91
-
92
- db = load_db()
93
-
94
- def track_chat(chat_id: int):
95
- if chat_id not in db["known_chats"]:
96
- db["known_chats"].append(chat_id)
97
- save_db(db)
98
-
99
- # ============================================================
100
- # МЕНЕДЖЕР КЛЮЧЕЙ GEMINI (Усталость и восстановление)
101
- # ============================================================
102
- def get_available_key(is_announce=False) -> Optional[str]:
103
- keys_pool = ANNOUNCE_KEYS if is_announce else CHAT_KEYS
104
- current_time = time.time()
105
-
106
- available_keys = []
107
- for key in keys_pool:
108
- usage_info = db["key_usage"].get(key, {"uses": 0, "cooldown_until": 0})
109
-
110
- # Если время кулдауна прошло, сбрасываем усталость
111
- if current_time > usage_info["cooldown_until"]:
112
- if usage_info["uses"] >= KEY_MAX_USES:
113
- usage_info["uses"] = 0 # Восстановил силы
114
- db["key_usage"][key] = usage_info
115
- save_db(db)
116
-
117
- if usage_info["uses"] < KEY_MAX_USES:
118
- available_keys.append(key)
119
-
120
- if not available_keys:
121
- return None
122
-
123
- return random.choice(available_keys)
124
 
125
- def increment_key_usage(key: str):
126
- usage_info = db["key_usage"].get(key, {"uses": 0, "cooldown_until": 0})
127
- usage_info["uses"] += 1
128
-
129
- if usage_info["uses"] >= KEY_MAX_USES:
130
- usage_info["cooldown_until"] = time.time() + KEY_COOLDOWN_SECONDS
 
131
 
132
- db["key_usage"][key] = usage_info
133
- save_db(db)
134
-
135
- async def ask_gemini(prompt: str, is_announce=False) -> str:
136
- key = get_available_key(is_announce)
137
- if not key:
138
- return "😔 Мои нейроны устали. Мне нужно время, чтобы восстановить силы. Я пока не могу отвечать."
 
139
 
140
- url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
141
- payload = {"contents": [{"parts": [{"text": prompt}]}]}
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  try:
144
  async with aiohttp.ClientSession() as session:
145
- async with session.post(url, json=payload) as resp:
146
  if resp.status == 200:
147
- increment_key_usage(key)
148
- data = await resp.json()
149
- return data['candidates'][0]['content']['parts'][0]['text']
150
- else:
151
- logger.error(f"Ошибка Gemini: {await resp.text()}")
152
- return "❌ Ошибка при обращении к мозгу Google."
153
  except Exception as e:
154
- logger.error(f"Сбой сети Gemini: {e}")
155
- return "❌ Произошла ошибка связи."
156
-
157
- # ============================================================
158
- # ГЕНЕРАЦИЯ ИЗОБРАЖЕНИЙ (Pollinations AI)
159
- # ============================================================
160
- async def generate_image(prompt: str) -> Optional[bytes]:
161
- encoded_prompt = urllib.parse.quote(prompt)
162
- url = f"https://image.pollinations.ai/prompt/{encoded_prompt}?nologo=true&seed={random.randint(1,99999)}&width=1024&height=1024&enhance=true"
163
- try:
164
- async with aiohttp.ClientSession() as session:
165
- async with session.get(url, timeout=120) as res:
166
- if res.status == 200:
167
- return await res.read()
168
- except Exception as e:
169
- logger.error(f"Ошибка генерации картинки: {e}")
170
  return None
171
 
172
- # ============================================================
173
- # ОБРАБОТЧИКИ TELEGRAM: АДМИН И ПАНЕЛЬ
174
- # ============================================================
175
- async def admin_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
176
  user_id = update.effective_user.id
177
- args = context.args
178
-
179
- if args and args[0] == ADMIN_PASSWORD:
180
- if user_id not in db["admins"]:
181
- db["admins"].append(user_id)
182
- if user_id not in db["whitelist"]:
183
- db["whitelist"].append(user_id) # Админ автоматически в вайтлисте
184
- save_db(db)
185
- await update.message.reply_text("✅ Пароль принят. Ты добавлен в список администраторов навсегда.")
186
- else:
187
- await update.message.reply_text("Ты и так уже админ!")
188
- else:
189
- await update.message.reply_text("❌ Неверный пароль.")
190
 
191
- def get_panel_keyboard():
192
- return InlineKeyboardMarkup([
193
- [InlineKeyboardButton("➕ Добавить в WhiteList", callback_data="panel_add")],
194
- [InlineKeyboardButton("➖ Удалить из WhiteList", callback_data="panel_remove")],
195
- [InlineKeyboardButton("📋 Показать WhiteList", callback_data="panel_show")]
196
- ])
197
 
198
- async def panel_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
 
 
 
 
199
  user_id = update.effective_user.id
200
- if user_id not in db["admins"]:
201
- await update.message.reply_text("⛔️ У тебя нет прав администратора.")
 
202
  return
203
-
204
- await update.message.reply_text(
205
- "🛠 **Админ Панель**\nЗдесь ты можешь управлять доступом к ИИ и генерации картинок.",
206
- reply_markup=get_panel_keyboard(),
207
- parse_mode=ParseMode.MARKDOWN
208
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
- async def handle_panel_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
211
- query = update.callback_query
212
- user_id = query.from_user.id
213
 
214
- if user_id not in db["admins"]:
215
- await query.answer("У тебя нет прав!", show_alert=True)
216
  return
217
-
218
- await query.answer()
219
- action = query.data
220
 
221
- if action == "panel_show":
222
- wl = "\n".join([f"- `{uid}`" for uid in db["whitelist"]]) if db["whitelist"] else "Пусто"
223
- await query.edit_message_text(f"📋 **WhiteList пользователей:**\n{wl}", reply_markup=get_panel_keyboard(), parse_mode=ParseMode.MARKDOWN)
224
-
225
- elif action == "panel_add":
226
- context.user_data["awaiting_id_for"] = "add"
227
- await query.message.reply_text("Отправь ID пользователя (число), чтобы добавить его в WhiteList:")
228
-
229
- elif action == "panel_remove":
230
- context.user_data["awaiting_id_for"] = "remove"
231
- await query.message.reply_text("Отправь ID пользователя (число), чтобы удалить его из WhiteList:")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
- # ============================================================
234
- # ОБРАБОТКА ОБЫЧНЫХ СООБЩЕНИЙ
235
- # ============================================================
236
- IMAGE_KEYWORDS = ["нарисуй", "нарисуйте", "рисуй", "draw", "картинка", "картинку", "фото", "изображение"]
237
- AI_TRIGGER_KEYWORDS = ["inai", "инэй", "инаи", "ии", "ai", "бот", "bot"]
238
 
239
- async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
240
- if not update.message or not update.message.text:
241
- return
242
 
243
- chat_id = update.effective_chat.id
 
 
 
 
 
 
 
 
 
 
 
 
244
  user_id = update.effective_user.id
245
- user_text = update.message.text.strip()
 
246
 
247
- # Сохраняем чат для ежечасной рассылки
248
- track_chat(chat_id)
249
-
250
- # 1. Логика Админ-панели (добавление/удаление ID)
251
- if context.user_data.get("awaiting_id_for"):
252
- action = context.user_data.pop("awaiting_id_for")
253
- if user_text.isdigit() or (user_text.startswith('-') and user_text[1:].isdigit()):
254
- target_id = int(user_text)
255
- if action == "add":
256
- if target_id not in db["whitelist"]:
257
- db["whitelist"].append(target_id)
258
- save_db(db)
259
- await update.message.reply_text(f"✅ ID {target_id} добавлен в WhiteList.")
260
- elif action == "remove":
261
- if target_id in db["whitelist"]:
262
- db["whitelist"].remove(target_id)
263
- save_db(db)
264
- await update.message.reply_text(f"🗑 ID {target_id} удален из WhiteList.")
265
- else:
266
- await update.message.reply_text("❌ Это не числовой ID. Операция отменена.")
267
  return
268
-
269
- # 2. Проверка прав (WhiteList) для работы с ИИ
270
- is_whitelisted = user_id in db["whitelist"]
271
-
272
- # 3. Генерация изображений (С ПРОЦЕНТАМИ)
273
- is_image_request = any(kw in user_text.lower() for kw in IMAGE_KEYWORDS)
274
- if is_image_request:
275
- if not is_whitelisted:
276
- await update.message.reply_text("⛔️ Извини, но ты не в белом списке для создания фотографий.")
277
  return
278
-
279
- status_msg = await update.message.reply_text("⏳ Подключаюсь к холсту... [0%]")
280
- await context.bot.send_chat_action(chat_id=chat_id, action=ChatAction.UPLOAD_PHOTO)
281
-
282
- await asyncio.sleep(1.5)
283
- await status_msg.edit_text("🎨 Рисую контуры... [35%]")
284
 
285
- await asyncio.sleep(1.5)
286
- await status_msg.edit_text("🖌 Добавляю цвета и детали... [75%]")
 
287
 
288
- image_bytes = await generate_image(user_text)
289
 
290
- await status_msg.edit_text("✨ Завершаю обработку... [99%]")
291
- await asyncio.sleep(1)
292
 
293
- await status_msg.delete()
294
- if image_bytes:
295
- await update.message.reply_photo(photo=io.BytesIO(image_bytes))
296
- else:
297
- await update.message.reply_text("❌ Упс, карандаш сломался. Не удалось нарисовать.")
298
- return
299
-
300
- # 4. Общение с Google Gemini (Текст)
301
- is_reply_to_bot = update.message.reply_to_message and update.message.reply_to_message.from_user.id == context.bot.id
302
- has_trigger = any(kw in user_text.lower() for kw in AI_TRIGGER_KEYWORDS)
303
- is_private = update.message.chat.type == "private"
304
-
305
- if is_private or is_reply_to_bot or has_trigger:
306
- if not is_whitelisted:
307
- # Молчим или говорим, что нет доступа. Выберем молчание, чтобы не спамить.
308
- return
309
 
310
- await context.bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
311
-
312
- # Системный промпт интегрируем в запрос
313
- prompt = (
314
- "Ты — умный и дружелюбный ИИ в Telegram. Отвечай кратко и интересно.\n"
315
- f"Запрос пользователя: {user_text}"
316
- )
317
-
318
- response_text = await ask_gemini(prompt, is_announce=False)
319
-
320
- try:
321
- await update.message.reply_text(response_text[:4000], parse_mode=ParseMode.MARKDOWN)
322
- except Exception:
323
- await update.message.reply_text(response_text[:4000])
324
-
325
- # ============================================================
326
- # ЕЖЕЧАСНАЯ РАССЫЛКА ВО ВСЕ ЧАТЫ
327
- # ============================================================
328
- async def hourly_announce_routine(app: Application):
329
- await asyncio.sleep(60) # Пауза перед первым запуском
330
- while True:
331
- chats = db["known_chats"].copy()
332
- if chats:
333
- prompt = (
334
- "Напиши короткое, забавное и приветливое сообщение для чата. "
335
- "Скажи всем, что ты ИИ от Гугла, присутствуешь здесь, готов пообщаться и "
336
- "что тебе очень хотелось бы по запросу нарисовать для них какое-нибудь классное фото! "
337
- "Не пиши длинно, 2-3 предложения."
338
- )
339
 
340
- # Генерируем текст используя ключи для анонсов
341
- announcement = await ask_gemini(prompt, is_announce=True)
 
 
342
 
343
- # Если ключи не устали
344
- if "Мои нейроны устали" not in announcement:
345
- for chat_id in chats:
346
- try:
347
- await app.bot.send_message(chat_id=chat_id, text=f"🔔 *Минутка внимания!*\n\n{announcement}", parse_mode=ParseMode.MARKDOWN)
348
- except Exception as e:
349
- logger.error(f"Не удалось отправить анонс в чат {chat_id}: {e}")
350
- await asyncio.sleep(1) # Защита от флуда в Telegram API
 
351
 
352
- await asyncio.sleep(3600) # Ждем 1 час
353
-
354
- # ============================================================
355
- # СТАНДАРТНЫЕ ФУНКЦИИ ВЕБХУКА И ЗАПУСКА
356
- # ============================================================
357
- async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
358
- await update.message.reply_text("👋 Привет! Я ИИ. Если ты в белом списке, я могу рисовать для тебя и общаться!")
359
- track_chat(update.effective_chat.id)
360
-
361
- telegram_app: Optional[Application] = None
362
-
363
- async def health_check(request: web.Request) -> web.Response:
364
- return web.Response(text="✅ Bot is running!", content_type="text/plain")
365
-
366
- async def webhook_handler(request: web.Request) -> web.Response:
367
- global telegram_app
368
- try:
369
- raw_data = await request.json()
370
- update_obj = Update.de_json(raw_data, telegram_app.bot)
371
- await telegram_app.process_update(update_obj)
372
- return web.Response(text="OK", status=200)
373
- except Exception as e:
374
- return web.Response(text="Error", status=500)
375
-
376
- async def build_web_app() -> web.Application:
377
- app = web.Application()
378
- app.router.add_get("/", health_check)
379
- app.router.add_post(f"/webhook/{BOT_TOKEN}", webhook_handler)
380
- return app
381
-
382
- async def main_async():
383
- global telegram_app
384
- cf_base_url = f"{CF_WORKER_URL.rstrip('/')}/bot"
385
- cf_base_file_url = f"{CF_WORKER_URL.rstrip('/')}/file/bot"
386
-
387
- telegram_app = (
388
- Application.builder()
389
- .token(BOT_TOKEN)
390
- .base_url(cf_base_url)
391
- .base_file_url(cf_base_file_url)
392
- .build()
393
- )
394
 
395
- telegram_app.add_handler(CommandHandler("start", start_command))
396
- telegram_app.add_handler(CommandHandler("admin", admin_command))
397
- telegram_app.add_handler(CommandHandler("panel", panel_command))
398
- telegram_app.add_handler(CallbackQueryHandler(handle_panel_callback, pattern="^panel_"))
399
- telegram_app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
400
-
401
- await telegram_app.initialize()
402
 
403
- webhook_full_url = f"{WEBHOOK_URL.rstrip('/')}/webhook/{BOT_TOKEN}"
404
- await telegram_app.bot.set_webhook(url=webhook_full_url)
405
- await telegram_app.start()
406
-
407
- # Запускаем фоновую задачу ежечасных уведомлений
408
- asyncio.create_task(hourly_announce_routine(telegram_app))
409
 
410
- web_app = await build_web_app()
411
- runner = web.AppRunner(web_app)
412
- await runner.setup()
413
- site = web.TCPSite(runner, host="0.0.0.0", port=PORT)
414
- await site.start()
415
 
416
- print(f"✅ Бот запущен! Webhook: {webhook_full_url}")
417
- try:
418
- await asyncio.Event().wait()
419
- finally:
420
- await telegram_app.bot.delete_webhook()
421
- await telegram_app.stop()
422
- await telegram_app.shutdown()
423
- await runner.cleanup()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
  if __name__ == "__main__":
426
- asyncio.run(main_async())
 
1
+ #!/usr/bin/env python3
2
  import os
3
  import asyncio
 
 
 
 
4
  import logging
5
+ from datetime import datetime, timedelta
6
+ from collections import deque
7
+ from typing import Dict, List, Optional
8
+
9
+ from telegram import Update
10
+ from telegram.ext import Application, CommandHandler, MessageHandler, ContextTypes, filters
11
+ import google.generativeai as genai
12
+ import firebase_admin
13
+ from firebase_admin import credentials, firestore
14
  import aiohttp
15
+ from duckduckgo_search import DDGS
16
 
17
+ logging.basicConfig(level=logging.INFO)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  logger = logging.getLogger(__name__)
19
 
20
+ # ============= КОНФИГУРАЦИЯ =============
21
+ TG_BOT_TOKEN = "8531345451:AAE8qJlFEwIcuQdUiIFk2viZS9xLLMk2c_o"
22
+ GOOGLE_API_KEYS = [
 
 
 
 
 
 
 
23
  "AIzaSyC59FksQAoeVeBKzVpxpL8zByEg3DAkKgE",
24
  "AIzaSyDCL7CImQjZ3DlInbjmWDWw7UeQ5bQDUbU"
25
  ]
26
+ CF_WORKER_URL = "https://tg-proxy.artyomanisimov37.workers.dev"
27
+ ADMIN_ID = 1234567890 # Замени на реальный ID админа
28
+ BOT_CHAT_ID = -1001234567890 # Замени на реальный ID чата
29
+ ACTIVITY_CHECK_INTERVAL = 1800 # 30 минут
30
+
31
+ # Firebase инициализация
32
+ try:
33
+ firebase_admin.initialize_app(credentials.Certificate("firebase_admin.json"))
34
+ db = firestore.client()
35
+ logger.info("✅ Firebase подключен")
36
+ except Exception as e:
37
+ logger.error(f"❌ Firebase ошибка: {e}")
38
+ db = None
39
+
40
+ # Google Generative AI
41
+ genai.configure(api_key=GOOGLE_API_KEYS[0])
42
+
43
+ # ============= СОСТОЯНИЕ БОТА =============
44
+ class BotState:
45
+ def __init__(self):
46
+ self.request_queue: Dict[int, deque] = {} # chat_id -> очередь запросов
47
+ self.processing: Dict[int, bool] = {} # chat_id -> обрабатывается ли
48
+ self.last_activity = datetime.now()
49
+ self.is_sleeping = False
50
+ self.whitelist: set = set()
51
+ self.current_api_key_index = 0
52
+ self.load_whitelist()
53
+
54
+ def load_whitelist(self):
55
+ """Загрузить белый список из Firebase"""
56
+ if db:
57
+ try:
58
+ doc = db.collection("bot_config").document("whitelist").get()
59
+ if doc.exists:
60
+ self.whitelist = set(doc.get("users", []))
61
+ else:
62
+ self.whitelist = {ADMIN_ID}
63
+ self.save_whitelist()
64
+ logger.info(f"📋 Белый список: {len(self.whitelist)} пользователей")
65
+ except Exception as e:
66
+ logger.error(f"Ошибка загрузки списка: {e}")
67
+ self.whitelist = {ADMIN_ID}
68
+
69
+ def save_whitelist(self):
70
+ """Сохранить белый список в Firebase"""
71
+ if db:
72
+ try:
73
+ db.collection("bot_config").document("whitelist").set({
74
+ "users": list(self.whitelist),
75
+ "updated_at": datetime.now()
76
+ })
77
+ except Exception as e:
78
+ logger.error(f"Ошибка сохранения: {e}")
79
+
80
+ def is_whitelisted(self, user_id: int) -> bool:
81
+ return user_id in self.whitelist or user_id == ADMIN_ID
82
+
83
+ def add_whitelist(self, user_id: int):
84
+ if user_id not in self.whitelist:
85
+ self.whitelist.add(user_id)
86
+ self.save_whitelist()
87
+
88
+ def remove_whitelist(self, user_id: int):
89
+ if user_id != ADMIN_ID and user_id in self.whitelist:
90
+ self.whitelist.remove(user_id)
91
+ self.save_whitelist()
92
 
93
+ bot_state = BotState()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ # ============= AI ГЕНЕРАТОР =============
96
+ async def generate_text(prompt: str, user_id: int) -> str:
97
+ """Генерировать текст через Google Generative AI"""
98
+ try:
99
+ model = genai.GenerativeModel("gemini-pro")
100
+ response = model.generate_content(prompt)
101
+ result = response.text
102
 
103
+ # Сохранить в Firebase
104
+ if db:
105
+ db.collection("users").document(str(user_id)).collection("history").add({
106
+ "prompt": prompt,
107
+ "response": result,
108
+ "timestamp": datetime.now(),
109
+ "model": "gemini-pro"
110
+ })
111
 
112
+ return result
113
+ except Exception as e:
114
+ logger.error(f"❌ Ошибка генерации: {e}")
115
+ return f"❌ Ошибка: {str(e)}"
116
+
117
+ # ============= CLOUDFLARE ПРОКСИ =============
118
+ async def send_via_cloudflare(method: str, chat_id: int, text: str, **kwargs):
119
+ """Отправить сообщение через Cloudflare worker"""
120
+ payload = {
121
+ "method": method,
122
+ "chat_id": chat_id,
123
+ "text": text,
124
+ **kwargs
125
+ }
126
 
127
  try:
128
  async with aiohttp.ClientSession() as session:
129
+ async with session.post(CF_WORKER_URL, json=payload) as resp:
130
  if resp.status == 200:
131
+ return await resp.json()
 
 
 
 
 
132
  except Exception as e:
133
+ logger.error(f"Cloudflare ошибка: {e}")
134
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  return None
136
 
137
+ # ============= КОМАНДЫ =============
138
+ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
 
 
139
  user_id = update.effective_user.id
140
+
141
+ if not bot_state.is_whitelisted(user_id):
142
+ await update.message.reply_text("❌ Вы не авторизованы")
143
+ return
144
+
145
+ text = """🤖 Добро пожаловать в AI Bot!
 
 
 
 
 
 
 
146
 
147
+ /ask [вопрос] - Спросить ИИ
148
+ /premium - О премиум подписке
149
+ /w - Управлять списком (админ)
150
+ /help - Справка
 
 
151
 
152
+ Просто напишите "ии [текст)" и я отвечу!"""
153
+
154
+ await update.message.reply_text(text)
155
+
156
+ async def ask(update: Update, context: ContextTypes.DEFAULT_TYPE):
157
  user_id = update.effective_user.id
158
+ chat_id = update.effective_chat.id
159
+
160
+ if not bot_state.is_whitelisted(user_id):
161
  return
162
+
163
+ if not context.args:
164
+ await update.message.reply_text("Использование: /ask опрос]")
165
+ return
166
+
167
+ prompt = " ".join(context.args)
168
+
169
+ # Добавить в очередь
170
+ if chat_id not in bot_state.request_queue:
171
+ bot_state.request_queue[chat_id] = deque()
172
+
173
+ bot_state.request_queue[chat_id].append({"user_id": user_id, "prompt": prompt})
174
+
175
+ # Показать индикатор печатания
176
+ await update.message.chat.send_action("typing")
177
+
178
+ # Генерировать ответ
179
+ response = await generate_text(prompt, user_id)
180
+
181
+ # Отправить ответ
182
+ if len(response) > 4096:
183
+ for i in range(0, len(response), 4096):
184
+ await update.message.reply_text(response[i:i+4096])
185
+ else:
186
+ await update.message.reply_text(response)
187
+
188
+ bot_state.last_activity = datetime.now()
189
 
190
+ async def whitelist_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
191
+ user_id = update.effective_user.id
 
192
 
193
+ if user_id != ADMIN_ID:
194
+ await update.message.reply_text(" Только админ")
195
  return
 
 
 
196
 
197
+ if not context.args:
198
+ users = "\n".join(str(u) for u in sorted(bot_state.whitelist))
199
+ await update.message.reply_text(f"📋 Список:\n{users}")
200
+ return
201
+
202
+ action = context.args[0]
203
+
204
+ if action == "add" and len(context.args) > 1:
205
+ try:
206
+ uid = int(context.args[1])
207
+ bot_state.add_whitelist(uid)
208
+ await update.message.reply_text(f"✅ Пользователь {uid} добавлен")
209
+ except ValueError:
210
+ await update.message.reply_text("❌ Неверный ID")
211
+
212
+ elif action == "remove" and len(context.args) > 1:
213
+ try:
214
+ uid = int(context.args[1])
215
+ bot_state.remove_whitelist(uid)
216
+ await update.message.reply_text(f"✅ Пользователь {uid} удален")
217
+ except ValueError:
218
+ await update.message.reply_text("❌ Неверный ID")
219
+
220
+ else:
221
+ await update.message.reply_text(
222
+ "/w - показать список\n"
223
+ "/w add [ID] - добавить\n"
224
+ "/w remove [ID] - удалить"
225
+ )
226
 
227
+ async def premium(update: Update, context: ContextTypes.DEFAULT_TYPE):
228
+ text = """💎 Премиум возможности:
229
+ Создание видео
230
+ Создание музыки
231
+ Приоритет в обработке
232
 
233
+ Дайте буст группе @chat_crx и получите премиум!"""
234
+ await update.message.reply_text(text)
 
235
 
236
+ async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
237
+ text = """📚 Справка:
238
+
239
+ /ask [текст] - Спросить ИИ
240
+ /imagine [описание] - Создать фото
241
+ /music [описание] - Создать музыку
242
+ /video [описание] - Создать видео
243
+ /premium - О премиум
244
+
245
+ Напишите "ии [текст] - я отвечу в чате"""
246
+ await update.message.reply_text(text)
247
+
248
+ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
249
  user_id = update.effective_user.id
250
+ chat_id = update.effective_chat.id
251
+ text = update.message.text
252
 
253
+ if not bot_state.is_whitelisted(user_id):
254
+ logger.warning(f"❌ Неавторизованный пользователь {user_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  return
256
+
257
+ # Если сообщение начинается с "ии"
258
+ if text.lower().startswith("ии"):
259
+ prompt = text[2:].strip()
260
+ if not prompt:
 
 
 
 
261
  return
 
 
 
 
 
 
262
 
263
+ # Добавить в очередь
264
+ if chat_id not in bot_state.request_queue:
265
+ bot_state.request_queue[chat_id] = deque()
266
 
267
+ bot_state.request_queue[chat_id].append({"user_id": user_id, "prompt": prompt})
268
 
269
+ # Обработать запрос
270
+ await update.message.chat.send_action("typing")
271
 
272
+ # Если это первый запрос, ответить сразу
273
+ if len(bot_state.request_queue[chat_id]) == 1:
274
+ response = await generate_text(prompt, user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
+ if len(response) > 4096:
277
+ for i in range(0, len(response), 4096):
278
+ await update.message.reply_text(response[i:i+4096])
279
+ else:
280
+ await update.message.reply_text(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
+ bot_state.request_queue[chat_id].popleft()
283
+ else:
284
+ # Для второго и последующих - подождать 5-10 секунд
285
+ await asyncio.sleep(7)
286
 
287
+ if bot_state.request_queue[chat_id]:
288
+ item = bot_state.request_queue[chat_id].popleft()
289
+ response = await generate_text(item["prompt"], item["user_id"])
290
+
291
+ if len(response) > 4096:
292
+ for i in range(0, len(response), 4096):
293
+ await update.message.reply_text(response[i:i+4096])
294
+ else:
295
+ await update.message.reply_text(response)
296
 
297
+ bot_state.last_activity = datetime.now()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
+ async def activity_monitor(context: ContextTypes.DEFAULT_TYPE):
300
+ """Мониторить активность каждые 30 минут"""
301
+ logger.info("🔍 Проверка активности")
 
 
 
 
302
 
303
+ if (datetime.now() - bot_state.last_activity).seconds > ACTIVITY_CHECK_INTERVAL:
304
+ bot_state.is_sleeping = True
305
+ logger.info("😴 Бот спит - нет активности")
306
+ else:
307
+ bot_state.is_sleeping = False
 
308
 
309
+ async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
310
+ logger.error(f"Ошибка: {context.error}")
 
 
 
311
 
312
+ # ============= ГЛАВНАЯ ФУНКЦИЯ =============
313
+ def main():
314
+ logger.info("🚀 Запуск Telegram AI Bot...")
315
+
316
+ app = Application.builder().token(TG_BOT_TOKEN).build()
317
+
318
+ # Команды
319
+ app.add_handler(CommandHandler("start", start))
320
+ app.add_handler(CommandHandler("ask", ask))
321
+ app.add_handler(CommandHandler("w", whitelist_cmd))
322
+ app.add_handler(CommandHandler("premium", premium))
323
+ app.add_handler(CommandHandler("help", help_cmd))
324
+
325
+ # Обычные сообщения
326
+ app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
327
+
328
+ # Обработчик ошибок
329
+ app.add_error_handler(error_handler)
330
+
331
+ # Job очередь для мониторинга
332
+ job_queue = app.job_queue
333
+ job_queue.run_repeating(activity_monitor, interval=ACTIVITY_CHECK_INTERVAL, first=60)
334
+
335
+ logger.info("✅ Бот запущен. Ctrl+C для остановки.")
336
+ app.run_polling()
337
 
338
  if __name__ == "__main__":
339
+ main()