Rid3 commited on
Commit
4deec56
·
verified ·
1 Parent(s): a01d360

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -32
app.py CHANGED
@@ -3,6 +3,10 @@ import sys
3
  import random
4
  import asyncio
5
  import threading
 
 
 
 
6
  from pyrogram import Client, filters, enums
7
  import google.generativeai as genai
8
  from flask import Flask, redirect
@@ -26,14 +30,21 @@ AI_CHAT_KEYS = [
26
  INFO_CHANNEL = "@g_bot_info" # Канал для уведомлений об исчерпании лимитов
27
  DOCS_URL = "https://rid3-quiz.web.app/docs/g-bot.html"
28
 
 
 
 
 
29
  app = Client("google_bot", session_string=SESSION_STRING, api_id=API_ID, api_hash=API_HASH)
30
 
 
 
 
31
  # --- ФУНКЦИЯ УМНОГО ОТВЕТА (АСИНХРОННАЯ С РОТАЦИЕЙ КЛЮЧЕЙ) ---
32
  async def get_ai_response(client, prompt, history_text):
 
33
  shuffled_keys = list(AI_CHAT_KEYS)
34
  random.shuffle(shuffled_keys)
35
 
36
- # Делаем поведение ИИ более "человечным" и естественным
37
  system_instruction = (
38
  "Ты — умный и живой ИИ-собеседник в Telegram (модель Gemini 2.5 Flash). "
39
  "Общайся естественно, лаконично, как человек. Без лишней воды, без странного форматирования "
@@ -46,13 +57,11 @@ async def get_ai_response(client, prompt, history_text):
46
  genai.configure(api_key=key)
47
  model = genai.GenerativeModel('gemini-2.5-flash')
48
 
49
- # Используем асинхронный вызов, чтобы бот не зависал для других пользователей
50
  response = await model.generate_content_async(full_prompt)
51
  return response.text.strip()
52
 
53
  except Exception as e:
54
  err_str = str(e).lower()
55
- # Проверяем ошибки связанные с лимитами
56
  if "429" in err_str or "quota" in err_str or "exhausted" in err_str:
57
  print(f"⚠️ Ключ {key[:10]}... исчерпал лимит.")
58
  continue
@@ -60,13 +69,14 @@ async def get_ai_response(client, prompt, history_text):
60
  print(f"❌ Ошибка API: {e}")
61
  continue
62
 
63
- # Если цикл закончился и мы здесь значит все ключи не сработали имит)
 
64
  try:
65
- await client.send_message(INFO_CHANNEL, "⚠️ **Внимание!** Лимит всех API-ключей Google Bot на сегодня полностью исчерпан.")
66
- except Exception as e:
67
- print(f"Не удалось написать в канал: {e}")
68
 
69
- return "Мои вычислительные ресурсы на сегодня исчерпаны (лимиты API). Я уже уведомил об этом в своем канале 😴"
70
 
71
  # --- ПОЛУЧЕНИЕ ИСТОРИИ ---
72
  async def fetch_history(chat_id):
@@ -80,7 +90,22 @@ async def fetch_history(chat_id):
80
  pass
81
  return "\n".join(reversed(messages))
82
 
83
- # --- КОМАНДЫ ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  @app.on_message(filters.command("рестарт", prefixes=".") & filters.me)
85
  async def restart_bot(client, message):
86
  await message.edit("🔄 **Перезагрузка ИИ...**")
@@ -88,6 +113,12 @@ async def restart_bot(client, message):
88
 
89
  @app.on_message(filters.text & ~filters.me)
90
  async def handle_bot(client, message):
 
 
 
 
 
 
91
  text = message.text
92
  chat_type = message.chat.type
93
  is_group = chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]
@@ -95,21 +126,17 @@ async def handle_bot(client, message):
95
  user_query = text
96
  should_respond = False
97
 
98
- # ТРИГГЕРЫ ДЛЯ ГРУПП
99
  triggers = ["@g ", "гугл ", "google ", "бот ", "bot "]
100
 
101
  if not is_group:
102
- # В личных сообщениях отвечаем всегда
103
  should_respond = True
104
  else:
105
- # 1. Отвечаем, если обратились по триггеру
106
  for t in triggers:
107
  if text.lower().startswith(t):
108
  should_respond = True
109
  user_query = text[len(t):].strip()
110
  break
111
 
112
- # 2. Отвечаем, если пользователь сделал "Reply" на наше сообщение
113
  if not should_respond and message.reply_to_message:
114
  if message.reply_to_message.from_user and message.reply_to_message.from_user.is_self:
115
  should_respond = True
@@ -134,40 +161,72 @@ async def handle_bot(client, message):
134
  action_taken = True
135
  except: pass
136
 
137
- # Если это была просто короткая команда ("закрепи" или "удали"), не дергаем нейросеть
138
  if action_taken and len(user_query.split()) <= 2:
139
  return
140
 
 
 
 
 
141
  # Эффект печати
142
- await client.send_chat_action(message.chat.id, enums.ChatAction.TYPING)
 
 
 
 
 
143
 
144
- # Получаем контекст и генерируем ответ
145
  history = await fetch_history(message.chat.id)
146
  ai_text = await get_ai_response(client, user_query, history)
147
 
148
- # --- ГЕНЕРАЦИЯ ФОТО ---
149
- photo_triggers = [арисуй", ото", "изображение", "сгенерируй"]
150
- if any(word in lower_query for word in photo_triggers):
151
- await client.send_chat_action(message.chat.id, enums.ChatAction.UPLOAD_PHOTO)
152
-
153
- # Формируем URL. Убираем лишние слова из запроса для Pollinations
154
- safe_query = user_query.replace(' ', '%20')
155
- photo_url = f"https://image.pollinations.ai/prompt/{safe_query}?width=1024&height=1024&nologo=true"
 
 
156
 
157
- # Telegram ограничивает подписи к фото (до 1024 символов)
158
- caption = ai_text[:1000]
159
  try:
160
- await message.reply_photo(photo_url, caption=caption)
161
- return
162
  except Exception:
163
- # Если генерация фото не удалась, просто отправляем текст
164
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
- # --- ОТПРАВКА ОТВЕТА ---
167
  await message.reply_text(ai_text)
168
 
169
 
170
- # === FLASK ДЛЯ ПОДДЕРЖАНИЯ АКТИВНОСТИ (Replit, Render, HuggingFace) ===
171
  flask_app = Flask(__name__)
172
 
173
  @flask_app.route('/')
 
3
  import random
4
  import asyncio
5
  import threading
6
+ import time
7
+ import re
8
+ import urllib.parse
9
+ import requests
10
  from pyrogram import Client, filters, enums
11
  import google.generativeai as genai
12
  from flask import Flask, redirect
 
30
  INFO_CHANNEL = "@g_bot_info" # Канал для уведомлений об исчерпании лимитов
31
  DOCS_URL = "https://rid3-quiz.web.app/docs/g-bot.html"
32
 
33
+ # Укажи здесь URL твоего сервера, который возвращает музыку (MP3/WAV) в ответ на GET-запрос.
34
+ # Пример запроса бота: http://твой-сервер.com/api/music?prompt=грустная%20мелодия
35
+ MUSIC_SERVER_URL = "http://your-music-server.com/api/music"
36
+
37
  app = Client("google_bot", session_string=SESSION_STRING, api_id=API_ID, api_hash=API_HASH)
38
 
39
+ # Глобальная переменная для состояния "усталости" (хранит время, до которого бот молчит)
40
+ TIRED_UNTIL = 0
41
+
42
  # --- ФУНКЦИЯ УМНОГО ОТВЕТА (АСИНХРОННАЯ С РОТАЦИЕЙ КЛЮЧЕЙ) ---
43
  async def get_ai_response(client, prompt, history_text):
44
+ global TIRED_UNTIL
45
  shuffled_keys = list(AI_CHAT_KEYS)
46
  random.shuffle(shuffled_keys)
47
 
 
48
  system_instruction = (
49
  "Ты — умный и живой ИИ-собеседник в Telegram (модель Gemini 2.5 Flash). "
50
  "Общайся естественно, лаконично, как человек. Без лишней воды, без странного форматирования "
 
57
  genai.configure(api_key=key)
58
  model = genai.GenerativeModel('gemini-2.5-flash')
59
 
 
60
  response = await model.generate_content_async(full_prompt)
61
  return response.text.strip()
62
 
63
  except Exception as e:
64
  err_str = str(e).lower()
 
65
  if "429" in err_str or "quota" in err_str or "exhausted" in err_str:
66
  print(f"⚠️ Ключ {key[:10]}... исчерпал лимит.")
67
  continue
 
69
  print(f"❌ Ошибка API: {e}")
70
  continue
71
 
72
+ # Если мы дошли до сюда, значит ВСЕ ключи выдали ошибку квоты. Бот уходит в спячку на час (3600 секунд).
73
+ TIRED_UNTIL = time.time() + 3600
74
  try:
75
+ await client.send_message(INFO_CHANNEL, "⚠️ **Внимание!** Лимит всех API-ключей Google Bot на сегодня полностью исчерпан. Я ушел в спячку на 1 час.")
76
+ except Exception:
77
+ pass
78
 
79
+ return "Я немного устал, я немного отдохну 😴"
80
 
81
  # --- ПОЛУЧЕНИЕ ИСТОРИИ ---
82
  async def fetch_history(chat_id):
 
90
  pass
91
  return "\n".join(reversed(messages))
92
 
93
+ # --- ЗАГРУЗКА МУЗЫКИ (Функция для работы в отдельном потоке) ---
94
+ def download_music(prompt_text):
95
+ safe_prompt = urllib.parse.quote(prompt_text)
96
+ url = f"{MUSIC_SERVER_URL}?prompt={safe_prompt}"
97
+ try:
98
+ response = requests.get(url, timeout=60)
99
+ if response.status_code == 200:
100
+ filename = f"music_{int(time.time())}.mp3"
101
+ with open(filename, "wb") as f:
102
+ f.write(response.content)
103
+ return filename
104
+ except Exception as e:
105
+ print(f"Ошибка скачивания музыки: {e}")
106
+ return None
107
+
108
+ # --- КОМАНДЫ И ОБРАБОТКА ---
109
  @app.on_message(filters.command("рестарт", prefixes=".") & filters.me)
110
  async def restart_bot(client, message):
111
  await message.edit("🔄 **Перезагрузка ИИ...**")
 
113
 
114
  @app.on_message(filters.text & ~filters.me)
115
  async def handle_bot(client, message):
116
+ global TIRED_UNTIL
117
+
118
+ # Если бот "устал", он полностью игнорирует сообщения до истечения таймера
119
+ if time.time() < TIRED_UNTIL:
120
+ return
121
+
122
  text = message.text
123
  chat_type = message.chat.type
124
  is_group = chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]
 
126
  user_query = text
127
  should_respond = False
128
 
 
129
  triggers = ["@g ", "гугл ", "google ", "бот ", "bot "]
130
 
131
  if not is_group:
 
132
  should_respond = True
133
  else:
 
134
  for t in triggers:
135
  if text.lower().startswith(t):
136
  should_respond = True
137
  user_query = text[len(t):].strip()
138
  break
139
 
 
140
  if not should_respond and message.reply_to_message:
141
  if message.reply_to_message.from_user and message.reply_to_message.from_user.is_self:
142
  should_respond = True
 
161
  action_taken = True
162
  except: pass
163
 
 
164
  if action_taken and len(user_query.split()) <= 2:
165
  return
166
 
167
+ # Проверка на конкретные команды для создания фото/музыки
168
+ photo_match = re.search(r'создай\s+фото\s+(.+)', user_query, re.IGNORECASE)
169
+ music_match = re.search(r'создай\s+музыку\s+(.+)', user_query, re.IGNORECASE)
170
+
171
  # Эффект печати
172
+ if photo_match:
173
+ await client.send_chat_action(message.chat.id, enums.ChatAction.UPLOAD_PHOTO)
174
+ elif music_match:
175
+ await client.send_chat_action(message.chat.id, enums.ChatAction.RECORD_AUDIO)
176
+ else:
177
+ await client.send_chat_action(message.chat.id, enums.ChatAction.TYPING)
178
 
179
+ # Получаем контекст и генерируем ответ от ИИ
180
  history = await fetch_history(message.chat.id)
181
  ai_text = await get_ai_response(client, user_query, history)
182
 
183
+ # Если ИИ вернул сообщение об усталости (произошло только что), отправляем его и завершаем
184
+ if ai_text == "Я немного устал, я немного отдохну 😴":
185
+ await message.reply_text(ai_text)
186
+ return
187
+
188
+ # --- ГЕНЕРАЦИЯ ФОТО ПО КОМАНДЕ ---
189
+ if photo_match:
190
+ photo_prompt = photo_match.group(1).strip()
191
+ safe_photo_prompt = urllib.parse.quote(photo_prompt)
192
+ photo_url = f"https://image.pollinations.ai/prompt/{safe_photo_prompt}?width=1024&height=1024&nologo=true"
193
 
 
 
194
  try:
195
+ await message.reply_photo(photo_url, caption=ai_text[:1000])
 
196
  except Exception:
197
+ await message.reply_text(ai_text)
198
+ return
199
+
200
+ # --- ГЕНЕРАЦИЯ МУЗЫКИ ПО КОМАНДЕ ---
201
+ if music_match:
202
+ music_prompt = music_match.group(1).strip()
203
+
204
+ if MUSIC_SERVER_URL == "http://your-music-server.com/api/music":
205
+ # Заглушка, если свой сервер еще не подключен
206
+ await message.reply_text(f"🎵 **[Сервер не настроен] Ищу музыку:** `{music_prompt}`\n\n_{ai_text}_")
207
+ return
208
+
209
+ # Загружаем музыку ��о своего сервера
210
+ downloaded_file = await asyncio.to_thread(download_music, music_prompt)
211
+
212
+ if downloaded_file:
213
+ try:
214
+ await message.reply_audio(downloaded_file, caption=ai_text[:1000])
215
+ except Exception:
216
+ await message.reply_text(f"Музыка сгенерирована, но произошла ошибка при отправке в Telegram.\n\n_{ai_text}_")
217
+ finally:
218
+ # Удаляем временный файл
219
+ if os.path.exists(downloaded_file):
220
+ os.remove(downloaded_file)
221
+ else:
222
+ await message.reply_text(f"❌ Мой сервер не смог сгенерировать музыку по запросу: `{music_prompt}`")
223
+ return
224
 
225
+ # --- СТАНДАРТНЫЙ ОТВЕТ ТЕКСТОМ ---
226
  await message.reply_text(ai_text)
227
 
228
 
229
+ # === FLASK ДЛЯ ПОДДЕРЖАНИЯ АКТИВНОСТИ ===
230
  flask_app = Flask(__name__)
231
 
232
  @flask_app.route('/')