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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -117
app.py CHANGED
@@ -27,107 +27,94 @@ AI_CHAT_KEYS = [
27
  "AIzaSyBjNK3MbrXnggTk2uzV3X-NK2PkT_oYB3M"
28
  ]
29
 
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
- бщайся естественно, лаконично, как человек. Без лишней воды, без странного форматирования "
51
- "и без приветствий в каждом сообщении. Отвечай строго по сути вопроса или поддерживай беседу."
 
 
52
  )
53
- full_prompt = f"ИНСТРУКЦИЯ:\n{system_instruction}\n\nКОНТЕКСТ ДИАЛОГА:\n{history_text}\n\nНОВОЕ СООБЩЕНИЕ:\n{prompt}"
 
54
 
55
  for key in shuffled_keys:
56
  try:
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
68
- else:
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):
83
  messages = []
84
  try:
85
- async for msg in app.get_chat_history(chat_id, limit=12):
86
- sender = msg.from_user.first_name if msg.from_user else "Собеседник"
87
- content = msg.text or msg.caption or "едиа/Стикер]"
 
88
  messages.append(f"{sender}: {content}")
89
- except:
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("🔄 **Перезагрузка ИИ...**")
112
- os.execl(sys.executable, sys.executable, *sys.argv)
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]
125
-
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:
@@ -136,107 +123,75 @@ async def handle_bot(client, message):
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
143
- user_query = text.strip()
144
 
145
- if not should_respond or not user_query:
146
- return
147
 
148
- # --- УПРАВЛЕНИЕ ЧАТОМ ---
149
- action_taken = False
150
  lower_query = user_query.lower()
151
-
152
  if "закрепи" in lower_query and message.reply_to_message:
153
- try:
154
- await client.pin_chat_message(message.chat.id, message.reply_to_message.id)
155
- action_taken = True
156
  except: pass
157
-
158
  if "удали" in lower_query and message.reply_to_message:
159
- try:
160
- await client.delete_messages(message.chat.id, message.reply_to_message.id)
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('/')
233
- def index():
234
- return redirect(DOCS_URL)
235
-
236
- def run_flask():
237
- flask_app.run(host="0.0.0.0", port=7860)
238
 
239
  if __name__ == "__main__":
240
  threading.Thread(target=run_flask, daemon=True).start()
241
- print("🤖 Бот успешно запущен!")
242
  app.run()
 
27
  "AIzaSyBjNK3MbrXnggTk2uzV3X-NK2PkT_oYB3M"
28
  ]
29
 
30
+ INFO_CHANNEL = "@g_bot_info"
31
  DOCS_URL = "https://rid3-quiz.web.app/docs/g-bot.html"
 
 
 
32
  MUSIC_SERVER_URL = "http://your-music-server.com/api/music"
33
 
34
  app = Client("google_bot", session_string=SESSION_STRING, api_id=API_ID, api_hash=API_HASH)
 
 
35
  TIRED_UNTIL = 0
36
 
37
+ # --- ФУНКЦИЯ УМНОГО ОТВЕТА ---
38
+ async def get_ai_response(client, prompt, history_text, is_media_request=False):
39
  global TIRED_UNTIL
40
  shuffled_keys = list(AI_CHAT_KEYS)
41
  random.shuffle(shuffled_keys)
42
 
43
+ # Новая, более жесткая инструкция, чтобы ИИ не говорил "я не могу"
44
  system_instruction = (
45
+ "Ты — Google Bot (модель Gemini 2.5 Flash). Ты ЖИВОЙ и УМНЫЙ. "
46
+ "ВАЖНО: Ты УМЕЕШЬ создавать изображения и музыку. Если тебя просят чтоо создать, "
47
+ "отвечай уверенно: онечно, сейчас сделаю', от твой запрос' или опиши, что ты рисуешь. "
48
+ "НИКОГДА не говори 'я не могу создавать изображения' или 'я текстовая модель'. "
49
+ "Твой стиль: лаконичный, дружелюбный, без формализма."
50
  )
51
+
52
+ full_prompt = f"СИСТЕМНАЯ УСТАНОВКА:\n{system_instruction}\n\nИСТОРИЯ ЧАТА:\n{history_text}\n\nПОЛЬЗОВАТЕЛЬ: {prompt}"
53
 
54
  for key in shuffled_keys:
55
  try:
56
  genai.configure(api_key=key)
57
  model = genai.GenerativeModel('gemini-2.5-flash')
 
58
  response = await model.generate_content_async(full_prompt)
59
+ text = response.text.strip()
60
+
61
+ # Фильтр "тупняка": если ИИ все равно выдал отказ, подменяем его
62
+ refusal_phrases = ["я не могу", "я всего лишь текстовая", "не умею создавать фото", "я не имею возможности"]
63
+ if is_media_request and any(phrase in text.lower() for phrase in refusal_phrases):
64
+ return "Конечно! Вот, что мне удалось создать по твоему запросу:"
65
+
66
+ return text
67
 
68
  except Exception as e:
69
  err_str = str(e).lower()
70
+ if any(x in err_str for x in ["429", "quota", "exhausted"]):
 
 
 
 
71
  continue
72
+ print(f"❌ Ошибка API: {e}")
73
+ continue
74
 
 
75
  TIRED_UNTIL = time.time() + 3600
76
  try:
77
+ await client.send_message(INFO_CHANNEL, "⚠️ Все API ключи сдохли. Отдыхаю 1 час.")
78
+ except: pass
 
 
79
  return "Я немного устал, я немн��го отдохну 😴"
80
 
81
  # --- ПОЛУЧЕНИЕ ИСТОРИИ ---
82
  async def fetch_history(chat_id):
83
  messages = []
84
  try:
85
+ # Уменьшил лимит до 10 для скорости и четкости
86
+ async for msg in app.get_chat_history(chat_id, limit=10):
87
+ sender = "Бот" if (msg.from_user and msg.from_user.is_self) else (msg.from_user.first_name if msg.from_user else "Юзер")
88
+ content = msg.text or "[Медиа]"
89
  messages.append(f"{sender}: {content}")
90
+ except: pass
 
91
  return "\n".join(reversed(messages))
92
 
 
93
  def download_music(prompt_text):
94
  safe_prompt = urllib.parse.quote(prompt_text)
95
  url = f"{MUSIC_SERVER_URL}?prompt={safe_prompt}"
96
  try:
97
+ response = requests.get(url, timeout=45)
98
  if response.status_code == 200:
99
  filename = f"music_{int(time.time())}.mp3"
100
+ with open(filename, "wb") as f: f.write(response.content)
 
101
  return filename
102
+ except: pass
 
103
  return None
104
 
105
+ # --- ОБРАБОТЧИК ---
 
 
 
 
 
106
  @app.on_message(filters.text & ~filters.me)
107
  async def handle_bot(client, message):
108
  global TIRED_UNTIL
109
+ if time.time() < TIRED_UNTIL: return
 
 
 
110
 
111
  text = message.text
112
+ is_group = message.chat.type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]
 
 
113
  user_query = text
114
  should_respond = False
115
 
116
  triggers = ["@g ", "гугл ", "google ", "бот ", "bot "]
117
+
118
  if not is_group:
119
  should_respond = True
120
  else:
 
123
  should_respond = True
124
  user_query = text[len(t):].strip()
125
  break
126
+ if not should_respond and message.reply_to_message and message.reply_to_message.from_user and message.reply_to_message.from_user.is_self:
127
+ should_respond = True
 
 
 
128
 
129
+ if not should_respond or not user_query: return
 
130
 
131
+ # Команды управления
 
132
  lower_query = user_query.lower()
 
133
  if "закрепи" in lower_query and message.reply_to_message:
134
+ try: await client.pin_chat_message(message.chat.id, message.reply_to_message.id)
 
 
135
  except: pass
136
+ return
137
  if "удали" in lower_query and message.reply_to_message:
138
+ try: await client.delete_messages(message.chat.id, message.reply_to_message.id)
 
 
139
  except: pass
 
 
140
  return
141
 
142
+ # Распознавание команд генерации
143
  photo_match = re.search(r'создай\s+фото\s+(.+)', user_query, re.IGNORECASE)
144
  music_match = re.search(r'создай\s+музыку\s+(.+)', user_query, re.IGNORECASE)
145
+ is_media = bool(photo_match or music_match)
146
 
147
+ # Индикация действия
148
+ action = enums.ChatAction.TYPING
149
+ if photo_match: action = enums.ChatAction.UPLOAD_PHOTO
150
+ elif music_match: action = enums.ChatAction.RECORD_AUDIO
151
+ await client.send_chat_action(message.chat.id, action)
 
 
152
 
 
153
  history = await fetch_history(message.chat.id)
154
+ ai_text = await get_ai_response(client, user_query, history, is_media_request=is_media)
155
 
156
+ if немного устал" in ai_text.lower():
 
157
  await message.reply_text(ai_text)
158
  return
159
 
160
+ # Логика отправки ФОТО
161
  if photo_match:
162
+ prompt = photo_match.group(1).strip()
163
+ url = f"https://image.pollinations.ai/prompt/{urllib.parse.quote(prompt)}?width=1024&height=1024&nologo=true&seed={random.randint(1, 99999)}"
 
 
164
  try:
165
+ await message.reply_photo(url, caption=ai_text[:1000])
166
+ except:
167
+ await message.reply_text(f"🖼 {ai_text}")
168
  return
169
 
170
+ # Логика отправки МУЗЫКИ
171
  if music_match:
172
+ prompt = music_match.group(1).strip()
173
+ if "your-music-server" in MUSIC_SERVER_URL:
174
+ await message.reply_text(f"🎵 Сервер музыки не настроен.\n\n{ai_text}")
 
 
175
  return
 
 
 
176
 
177
+ file = await asyncio.to_thread(download_music, prompt)
178
+ if file:
179
+ try: await message.reply_audio(file, caption=ai_text[:1000])
180
+ except: await message.reply_text(ai_text)
181
+ finally:
182
+ if os.path.exists(file): os.remove(file)
 
 
 
183
  else:
184
+ await message.reply_text("Не удалось сгенерировать музыку, попробуй позже.")
185
  return
186
 
 
187
  await message.reply_text(ai_text)
188
 
189
+ # === FLASK ===
 
190
  flask_app = Flask(__name__)
 
191
  @flask_app.route('/')
192
+ def index(): return redirect(DOCS_URL)
193
+ def run_flask(): flask_app.run(host="0.0.0.0", port=7860)
 
 
 
194
 
195
  if __name__ == "__main__":
196
  threading.Thread(target=run_flask, daemon=True).start()
 
197
  app.run()