Rid3 commited on
Commit
b202111
·
verified ·
1 Parent(s): 8065dd2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +264 -107
app.py CHANGED
@@ -1,5 +1,4 @@
1
  import os
2
- import sys
3
  import random
4
  import asyncio
5
  import threading
@@ -27,94 +26,189 @@ 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
- 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
- вой стиль: лаконичный, дружелюбный, без формализма. твой создатель RID3"
 
 
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,75 +217,138 @@ async def handle_bot(client, message):
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()
 
1
  import os
 
2
  import random
3
  import asyncio
4
  import threading
 
26
  "AIzaSyBjNK3MbrXnggTk2uzV3X-NK2PkT_oYB3M"
27
  ]
28
 
29
+ INFO_CHANNEL = "@g_bot_info"
30
  DOCS_URL = "https://rid3-quiz.web.app/docs/g-bot.html"
 
31
 
32
+ # ============================================================
33
+ # МОДЕЛИ
34
+ # Фото (Pollinations image API):
35
+ # flux — стандартное качество (по умолчанию)
36
+ # nano — лёгкая быстрая модель
37
+ # banana — реалистичный стиль (flux-realism)
38
+ #
39
+ # Аудио (Pollinations audio API):
40
+ # lyra — нейронная аудио-модель Google DeepMind
41
+ # ============================================================
42
+ PHOTO_MODELS: dict[str, str] = {
43
+ "flux": "flux",
44
+ "nano": "flux-anime", # nano = аниме / лёгкий стиль
45
+ "banana": "flux-realism", # banana = фото-реализм
46
+ "turbo": "turbo",
47
+ }
48
+ DEFAULT_PHOTO_MODEL = "flux"
49
 
50
+ AUDIO_MODEL = "lyra"
51
+
52
+ app = Client(
53
+ "google_bot",
54
+ session_string=SESSION_STRING,
55
+ api_id=API_ID,
56
+ api_hash=API_HASH,
57
+ )
58
+ TIRED_UNTIL: float = 0.0
59
+
60
+
61
+ # ============================================================
62
+ # ГЕНЕРАЦИЯ ФОТО
63
+ # ============================================================
64
+ async def fetch_image_bytes(prompt: str, model: str = DEFAULT_PHOTO_MODEL) -> bytes | None:
65
+ seed = random.randint(1, 99999)
66
+ model_id = PHOTO_MODELS.get(model.lower(), model)
67
+ encoded = urllib.parse.quote(prompt)
68
+ url = (
69
+ f"https://image.pollinations.ai/prompt/{encoded}"
70
+ f"?width=1024&height=1024&nologo=true&seed={seed}&model={model_id}"
71
+ )
72
+ print(f"🖼 Фото: model={model_id} prompt={prompt[:60]}")
73
+ try:
74
+ resp = await asyncio.to_thread(requests.get, url, timeout=90)
75
+ if resp.status_code == 200 and "image" in resp.headers.get("Content-Type", ""):
76
+ return resp.content
77
+ print(f"⚠️ Фото: HTTP {resp.status_code}")
78
+ except Exception as e:
79
+ print(f"❌ Ошибка фото: {e}")
80
+ return None
81
+
82
+
83
+ # ============================================================
84
+ # ГЕНЕРАЦИЯ АУДИО — Lyra
85
+ # ============================================================
86
+ async def fetch_audio_bytes(prompt: str) -> bytes | None:
87
+ encoded = urllib.parse.quote(prompt)
88
+ endpoints = [
89
+ f"https://audio.pollinations.ai/{encoded}?model=lyra",
90
+ f"https://audio.pollinations.ai/{encoded}?model=musicgen-stereo-large",
91
+ f"https://audio.pollinations.ai/{encoded}",
92
+ ]
93
+ for url in endpoints:
94
+ print(f"🎵 Аудио: {url[:80]}")
95
+ try:
96
+ resp = await asyncio.to_thread(requests.get, url, timeout=120)
97
+ ct = resp.headers.get("Content-Type", "")
98
+ is_audio = (
99
+ "audio" in ct
100
+ or resp.content[:3] == b"ID3"
101
+ or resp.content[:4] == b"fLaC"
102
+ )
103
+ if resp.status_code == 200 and is_audio:
104
+ print(f"✅ Аудио: {len(resp.content)} байт")
105
+ return resp.content
106
+ print(f"⚠️ Аудио: HTTP {resp.status_code} CT={ct}")
107
+ except Exception as e:
108
+ print(f"❌ Ошибка аудио: {e}")
109
+ return None
110
+
111
+
112
+ # ============================================================
113
+ # ИИ
114
+ # ============================================================
115
+ async def get_ai_response(
116
+ client, prompt: str, history_text: str, is_media_request: bool = False
117
+ ) -> str:
118
  global TIRED_UNTIL
119
+ keys = list(AI_CHAT_KEYS)
120
+ random.shuffle(keys)
121
+
 
122
  system_instruction = (
123
+ "Ты — Google Bot (Gemini 2.5 Flash). Умный, лаконичный, др��желюбный. "
124
+ сли создаёшь медиа — коротко опиши результат. Создатель: RID3."
125
+ )
126
+ full_prompt = (
127
+ f"СИСТЕМНАЯ УСТАНОВКА:\n{system_instruction}\n\n"
128
+ f"ИСТОРИЯ:\n{history_text}\n\n"
129
+ f"ПОЛЬЗОВАТЕЛЬ: {prompt}"
130
  )
131
+ refusals = [
132
+ не могу", всего лишь текстовая", "не умею создавать",
133
+ "я не имею возможности", "невозможно создать", "я языковая модель",
134
+ ]
135
 
136
+ for key in keys:
137
  try:
138
  genai.configure(api_key=key)
139
+ model = genai.GenerativeModel("gemini-2.5-flash")
140
+ resp = await model.generate_content_async(full_prompt)
141
+ text = resp.text.strip()
142
+ if is_media_request and any(p in text.lower() for p in refusals):
143
+ return "Готово! Вот что я создал ✨"
 
 
 
 
144
  return text
 
145
  except Exception as e:
146
+ if any(x in str(e).lower() for x in ["429", "quota", "exhausted", "resource_exhausted"]):
 
147
  continue
148
+ print(f"❌ API: {e}")
149
+
150
+ TIRED_UNTIL = time.time() + 3600
 
151
  try:
152
+ await client.send_message(INFO_CHANNEL, "⚠️ Все ключи исчерпаны. Отдыхаю 1 ч.")
153
+ except Exception:
154
+ pass
155
+ return "Я немного устал, отдохну 😴"
156
+
157
 
158
+ # ============================================================
159
+ # ИСТОРИЯ
160
+ # ============================================================
161
+ async def fetch_history(chat_id: int) -> str:
162
+ msgs: list[str] = []
163
  try:
 
164
  async for msg in app.get_chat_history(chat_id, limit=10):
165
+ sender = "Бот" if (msg.from_user and msg.from_user.is_self) \
166
+ else (msg.from_user.first_name if msg.from_user else "Юзер")
167
+ content = msg.text or msg.caption or "[Медиа]"
168
+ msgs.append(f"{sender}: {content}")
169
+ except Exception:
170
+ pass
171
+ return "\n".join(reversed(msgs))
172
+
173
+
174
+ # ============================================================
175
+ # ПАРСИНГ МОДЕЛИ ИЗ ЗАПРОСА
176
+ # "нарисуй nano кот" → (nano, "кот")
177
+ # "нарисуй banana girl"(banana, "girl")
178
+ # "нарисуй дракон" → (flux, "дракон")
179
+ # ============================================================
180
+ def parse_photo_prompt(raw: str) -> tuple[str, str]:
181
+ pattern = r'^(' + '|'.join(PHOTO_MODELS.keys()) + r')\s+(.+)$'
182
+ m = re.match(pattern, raw.strip(), re.IGNORECASE)
183
+ if m:
184
+ return m.group(1).lower(), m.group(2).strip()
185
+ return DEFAULT_PHOTO_MODEL, raw.strip()
186
+
187
+
188
+ MODEL_LABELS: dict[str, str] = {
189
+ "flux": "Flux ⚡",
190
+ "nano": "Nano 🔬",
191
+ "banana": "Banana 🍌",
192
+ "turbo": "Turbo 🚀",
193
+ }
194
+
195
 
196
+ # ============================================================
197
+ # ОБРАБОТЧИК
198
+ # ============================================================
199
  @app.on_message(filters.text & ~filters.me)
200
  async def handle_bot(client, message):
201
  global TIRED_UNTIL
202
+ if time.time() < TIRED_UNTIL:
203
+ return
204
 
205
+ text: str = message.text or ""
206
  is_group = message.chat.type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]
207
  user_query = text
208
  should_respond = False
209
+
210
  triggers = ["@g ", "гугл ", "google ", "бот ", "bot "]
211
+
212
  if not is_group:
213
  should_respond = True
214
  else:
 
217
  should_respond = True
218
  user_query = text[len(t):].strip()
219
  break
220
+ if (
221
+ not should_respond
222
+ and message.reply_to_message
223
+ and message.reply_to_message.from_user
224
+ and message.reply_to_message.from_user.is_self
225
+ ):
226
  should_respond = True
227
 
228
+ if not should_respond or not user_query.strip():
229
+ return
230
+
231
+ lower = user_query.lower()
232
 
233
+ # --- Модерация ---
234
+ if "закрепи" in lower and message.reply_to_message:
235
+ try:
236
+ await client.pin_chat_message(message.chat.id, message.reply_to_message.id)
237
+ except Exception:
238
+ pass
239
  return
240
+
241
+ if "удали" in lower and message.reply_to_message:
242
+ try:
243
+ await client.delete_messages(message.chat.id, message.reply_to_message.id)
244
+ except Exception:
245
+ pass
246
  return
247
 
248
+ # --- Паттерны генерации ---
249
+ photo_match = re.search(
250
+ r'(?:создай\s+фото|нарисуй|сгенерируй\s+фото|сделай\s+фото|draw|generate\s+image)\s+(.+)',
251
+ user_query, re.IGNORECASE,
252
+ )
253
+ music_match = re.search(
254
+ r'(?:создай\s+музыку|сгенерируй\s+музыку|напиши\s+музыку|compose|generate\s+music|lyra)\s+(.+)',
255
+ user_query, re.IGNORECASE,
256
+ )
257
 
258
+ is_media = bool(photo_match or music_match)
259
+ action = (
260
+ enums.ChatAction.UPLOAD_PHOTO if photo_match else
261
+ enums.ChatAction.RECORD_AUDIO if music_match else
262
+ enums.ChatAction.TYPING
263
+ )
264
  await client.send_chat_action(message.chat.id, action)
265
+
266
  history = await fetch_history(message.chat.id)
267
  ai_text = await get_ai_response(client, user_query, history, is_media_request=is_media)
268
 
269
+ if "немного устал" in ai_text.lower():
270
  await message.reply_text(ai_text)
271
  return
272
 
273
+ # ============================================================
274
+ # ФОТО — nano / banana / flux / turbo
275
+ # ============================================================
276
  if photo_match:
277
+ raw_prompt = photo_match.group(1).strip()
278
+ photo_model, prompt = parse_photo_prompt(raw_prompt)
279
+ label = MODEL_LABELS.get(photo_model, photo_model.capitalize())
280
+
281
+ await client.send_chat_action(message.chat.id, enums.ChatAction.UPLOAD_PHOTO)
282
+ img = await fetch_image_bytes(prompt, model=photo_model)
283
+
284
+ if img:
285
+ tmp = f"/tmp/img_{int(time.time())}_{random.randint(1000,9999)}.jpg"
286
+ try:
287
+ with open(tmp, "wb") as f:
288
+ f.write(img)
289
+ caption = f"[{label}] {ai_text}"[:1024] if ai_text else f"[{label}] {prompt}"
290
+ await message.reply_photo(tmp, caption=caption)
291
+ except Exception as e:
292
+ print(f"❌ Отправка фото: {e}")
293
+ await message.reply_text(f"Не удалось отправить фото 😞\n{ai_text}")
294
+ finally:
295
+ if os.path.exists(tmp):
296
+ os.remove(tmp)
297
+ else:
298
+ await message.reply_text(f"Не удалось сгенерировать изображение 😞\n{ai_text}")
299
  return
300
 
301
+ # ============================================================
302
+ # АУДИО — Lyra
303
+ # ============================================================
304
  if music_match:
305
  prompt = music_match.group(1).strip()
306
+
307
+ await client.send_chat_action(message.chat.id, enums.ChatAction.RECORD_AUDIO)
308
+ audio = await fetch_audio_bytes(prompt)
309
+
310
+ if audio:
311
+ tmp = f"/tmp/audio_{int(time.time())}_{random.randint(1000,9999)}.mp3"
312
+ try:
313
+ with open(tmp, "wb") as f:
314
+ f.write(audio)
315
+ caption = f"[Lyra 🎵] {ai_text}"[:1024] if ai_text else f"[Lyra 🎵] {prompt}"
316
+ await message.reply_audio(
317
+ tmp,
318
+ caption=caption,
319
+ title=prompt[:64],
320
+ performer="Google Bot × Lyra",
321
+ )
322
+ except Exception as e:
323
+ print(f"❌ Отправка аудио: {e}")
324
+ await message.reply_text(f"Не удалось отправить аудио 😞\n{ai_text}")
325
+ finally:
326
+ if os.path.exists(tmp):
327
+ os.remove(tmp)
328
  else:
329
+ await message.reply_text(f"[Lyra] Временно недоступна.\n\n{ai_text}")
330
  return
331
 
332
+ # ============================================================
333
+ # ТЕКСТ
334
+ # ============================================================
335
  await message.reply_text(ai_text)
336
 
337
+
338
+ # ============================================================
339
+ # FLASK
340
+ # ============================================================
341
  flask_app = Flask(__name__)
342
+
343
+ @flask_app.route("/")
344
+ def index():
345
+ return redirect(DOCS_URL)
346
+
347
+ def run_flask():
348
+ flask_app.run(host="0.0.0.0", port=7860)
349
+
350
 
351
  if __name__ == "__main__":
352
  threading.Thread(target=run_flask, daemon=True).start()
353
+ print("🤖 Google Bot | модели: flux / nano / banana / lyra")
354
  app.run()