Rid3 commited on
Commit
50fec23
·
verified ·
1 Parent(s): ff39ab8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -37
app.py CHANGED
@@ -3,13 +3,13 @@ import asyncio
3
  import re
4
  import urllib.parse
5
  import io
6
- import traceback
7
- import requests
8
- import torch
9
  import logging
10
  import random
11
  import json
12
- from typing import Optional, Dict, List, Tuple
 
 
 
13
  from telegram import (
14
  Update,
15
  InlineKeyboardButton,
@@ -37,7 +37,8 @@ logger = logging.getLogger(__name__)
37
  # ============================================================
38
  # КОНФИГУРАЦИЯ БОТА
39
  # ============================================================
40
- BOT_TOKEN = "8605889873:AAE2gV2t0psXKlj-8h9ksyyoCrWa8Q-TdkA"
 
41
 
42
  # КЛЮЧИ GEMINI
43
  GEMINI_API_KEYS = [
@@ -64,24 +65,22 @@ AI_TRIGGER_KEYWORDS = ["inai", "инэй", "инаи", "ии", "ai", "бот", "
64
  IMAGE_KEYWORDS = ["нарисуй", "рисуй", "draw", "картинка", "фото"]
65
 
66
  # ============================================================
67
- # РАБОТА С GEMINI API
68
  # ============================================================
69
 
70
  async def get_next_key():
71
  global current_key_index
72
  async with key_lock:
73
  key = GEMINI_API_KEYS[current_key_index]
 
74
  current_key_index = (current_key_index + 1) % len(GEMINI_API_KEYS)
75
- return key, (current_key_index - 1) % len(GEMINI_API_KEYS)
76
 
77
  async def call_gemini_api(prompt: str, system_prompt: str = None, history: list = None) -> str:
78
  if system_prompt is None:
79
  system_prompt = INAI_SYSTEM_PROMPT
80
 
81
- messages = []
82
- if history:
83
- for item in history:
84
- messages.append(item)
85
  messages.append({"role": "user", "parts": [{"text": prompt}]})
86
 
87
  request_body = {
@@ -91,21 +90,35 @@ async def call_gemini_api(prompt: str, system_prompt: str = None, history: list
91
  }
92
 
93
  attempts = 0
94
- while attempts < len(GEMINI_API_KEYS):
95
- key, key_idx = await get_next_key()
96
- attempts += 1
97
- url = GEMINI_API_URL.format(model=GEMINI_MODEL, key=key)
98
- try:
99
- response = await asyncio.to_thread(
100
- lambda: requests.post(url, json=request_body, timeout=30)
101
- )
102
- if response.status_code == 200:
103
- return response.json()["candidates"][0]["content"]["parts"][0]["text"]
104
- logger.warning(f"Ключ {key_idx} ошибка {response.status_code}")
105
- except Exception as e:
106
- logger.error(f"Ошибка API: {e}")
 
 
 
107
  return "❌ Ошибка сервиса Gemini. Попробуйте позже."
108
 
 
 
 
 
 
 
 
 
 
 
 
109
  # ============================================================
110
  # ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
111
  # ============================================================
@@ -136,13 +149,6 @@ def build_inline_keyboard(buttons: list) -> Optional[InlineKeyboardMarkup]:
136
  keyboard.append([InlineKeyboardButton(btn.get("text", "OK"), callback_data=f"ai_action:{btn.get('action', 'none')[:40]}")])
137
  return InlineKeyboardMarkup(keyboard)
138
 
139
- async def generate_image(prompt: str) -> Optional[bytes]:
140
- try:
141
- url = f"https://image.pollinations.ai/prompt/{urllib.parse.quote(prompt)}?nologo=true&seed={random.randint(1,999)}"
142
- res = await asyncio.to_thread(lambda: requests.get(url, timeout=40))
143
- return res.content if res.status_code == 200 else None
144
- except: return None
145
-
146
  # ============================================================
147
  # ИСТОРИЯ ЧАТА
148
  # ============================================================
@@ -151,7 +157,9 @@ chat_history: Dict[int, list] = {}
151
  def add_to_history(chat_id: int, role: str, text: str):
152
  if chat_id not in chat_history: chat_history[chat_id] = []
153
  chat_history[chat_id].append({"role": role, "parts": [{"text": text}]})
154
- if len(chat_history[chat_id]) > 10: chat_history[chat_id].pop(0)
 
 
155
 
156
  # ============================================================
157
  # ОБРАБОТЧИКИ
@@ -166,7 +174,6 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
166
  chat_id = update.effective_chat.id
167
  text = update.message.text
168
 
169
- # Логика ответа (упрощенно для полной версии)
170
  is_group = update.message.chat.type in ["group", "supergroup"]
171
  is_reply = update.message.reply_to_message and update.message.reply_to_message.from_user.is_bot
172
 
@@ -176,8 +183,11 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
176
  await context.bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
177
  status = await update.message.reply_text("💠 Думаю...")
178
 
 
 
 
179
  add_to_history(chat_id, "user", text)
180
- ai_resp = await call_gemini_api(text, history=chat_history.get(chat_id)[:-1])
181
 
182
  clean_text, buttons, draw_prompt = parse_ai_response(ai_resp)
183
  add_to_history(chat_id, "model", clean_text)
@@ -187,10 +197,26 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
187
  img = await generate_image(draw_prompt if draw_prompt else text)
188
  if img:
189
  await status.delete()
190
- await update.message.reply_photo(photo=io.BytesIO(img), caption=clean_text[:1000], reply_markup=build_inline_keyboard(buttons))
 
 
 
 
191
  return
192
 
193
- await status.edit_text(clean_text[:4090], reply_markup=build_inline_keyboard(buttons), parse_mode=ParseMode.MARKDOWN)
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
  async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
196
  query = update.callback_query
@@ -201,6 +227,25 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
201
  async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE):
202
  logger.error(f"Error: {context.error}")
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  # ============================================================
205
  # MAIN - ЗАПУСК
206
  # ============================================================
@@ -208,10 +253,11 @@ async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE):
208
  def main():
209
  print("🚀 Starting INAI Bot on Hugging Face...")
210
 
211
- # Настройка с МАКСИМАЛЬНЫМИ таймаутами для облака
212
  application = (
213
  Application.builder()
214
  .token(BOT_TOKEN)
 
215
  .connect_timeout(60.0)
216
  .read_timeout(60.0)
217
  .write_timeout(60.0)
 
3
  import re
4
  import urllib.parse
5
  import io
 
 
 
6
  import logging
7
  import random
8
  import json
9
+ import aiohttp
10
+ from typing import Optional, Dict
11
+ from aiohttp import web
12
+
13
  from telegram import (
14
  Update,
15
  InlineKeyboardButton,
 
37
  # ============================================================
38
  # КОНФИГУРАЦИЯ БОТА
39
  # ============================================================
40
+ # РЕКОМЕНДАЦИЯ: В будущем лучше вынести токены в Secrets (переменные окружения) Hugging Face
41
+ BOT_TOKEN = os.environ.get("BOT_TOKEN", "8605889873:AAE2gV2t0psXKlj-8h9ksyyoCrWa8Q-TdkA")
42
 
43
  # КЛЮЧИ GEMINI
44
  GEMINI_API_KEYS = [
 
65
  IMAGE_KEYWORDS = ["нарисуй", "рисуй", "draw", "картинка", "фото"]
66
 
67
  # ============================================================
68
+ # РАБОТА С GEMINI И ИЗОБРАЖЕНИЯМИ (ЧЕРЕЗ AIOHTTP)
69
  # ============================================================
70
 
71
  async def get_next_key():
72
  global current_key_index
73
  async with key_lock:
74
  key = GEMINI_API_KEYS[current_key_index]
75
+ idx = current_key_index
76
  current_key_index = (current_key_index + 1) % len(GEMINI_API_KEYS)
77
+ return key, idx
78
 
79
  async def call_gemini_api(prompt: str, system_prompt: str = None, history: list = None) -> str:
80
  if system_prompt is None:
81
  system_prompt = INAI_SYSTEM_PROMPT
82
 
83
+ messages = history.copy() if history else []
 
 
 
84
  messages.append({"role": "user", "parts": [{"text": prompt}]})
85
 
86
  request_body = {
 
90
  }
91
 
92
  attempts = 0
93
+ async with aiohttp.ClientSession() as session:
94
+ while attempts < len(GEMINI_API_KEYS):
95
+ key, key_idx = await get_next_key()
96
+ attempts += 1
97
+ url = GEMINI_API_URL.format(model=GEMINI_MODEL, key=key)
98
+
99
+ try:
100
+ async with session.post(url, json=request_body, timeout=30) as response:
101
+ if response.status == 200:
102
+ data = await response.json()
103
+ return data["candidates"][0]["content"]["parts"][0]["text"]
104
+ else:
105
+ logger.warning(f"Ключ {key_idx} вернул статус {response.status}")
106
+ except Exception as e:
107
+ logger.error(f"Ошибка API Gemini: {e}")
108
+
109
  return "❌ Ошибка сервиса Gemini. Попробуйте позже."
110
 
111
+ async def generate_image(prompt: str) -> Optional[bytes]:
112
+ url = f"https://image.pollinations.ai/prompt/{urllib.parse.quote(prompt)}?nologo=true&seed={random.randint(1,999)}"
113
+ try:
114
+ async with aiohttp.ClientSession() as session:
115
+ async with session.get(url, timeout=40) as res:
116
+ if res.status == 200:
117
+ return await res.read()
118
+ except Exception as e:
119
+ logger.error(f"Ошибка генерации картинки: {e}")
120
+ return None
121
+
122
  # ============================================================
123
  # ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
124
  # ============================================================
 
149
  keyboard.append([InlineKeyboardButton(btn.get("text", "OK"), callback_data=f"ai_action:{btn.get('action', 'none')[:40]}")])
150
  return InlineKeyboardMarkup(keyboard)
151
 
 
 
 
 
 
 
 
152
  # ============================================================
153
  # ИСТОРИЯ ЧАТА
154
  # ============================================================
 
157
  def add_to_history(chat_id: int, role: str, text: str):
158
  if chat_id not in chat_history: chat_history[chat_id] = []
159
  chat_history[chat_id].append({"role": role, "parts": [{"text": text}]})
160
+ # Храним последние 10 сообщений
161
+ if len(chat_history[chat_id]) > 10:
162
+ chat_history[chat_id].pop(0)
163
 
164
  # ============================================================
165
  # ОБРАБОТЧИКИ
 
174
  chat_id = update.effective_chat.id
175
  text = update.message.text
176
 
 
177
  is_group = update.message.chat.type in ["group", "supergroup"]
178
  is_reply = update.message.reply_to_message and update.message.reply_to_message.from_user.is_bot
179
 
 
183
  await context.bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
184
  status = await update.message.reply_text("💠 Думаю...")
185
 
186
+ # Получаем историю без последнего сообщения пользователя
187
+ history = chat_history.get(chat_id, [])
188
+
189
  add_to_history(chat_id, "user", text)
190
+ ai_resp = await call_gemini_api(text, history=history)
191
 
192
  clean_text, buttons, draw_prompt = parse_ai_response(ai_resp)
193
  add_to_history(chat_id, "model", clean_text)
 
197
  img = await generate_image(draw_prompt if draw_prompt else text)
198
  if img:
199
  await status.delete()
200
+ await update.message.reply_photo(
201
+ photo=io.BytesIO(img),
202
+ caption=clean_text[:1000],
203
+ reply_markup=build_inline_keyboard(buttons)
204
+ )
205
  return
206
 
207
+ # Защита от падений из-за неправильного Markdown, сгенерированного ИИ
208
+ try:
209
+ await status.edit_text(
210
+ clean_text[:4090],
211
+ reply_markup=build_inline_keyboard(buttons),
212
+ parse_mode=ParseMode.MARKDOWN
213
+ )
214
+ except Exception as e:
215
+ logger.warning(f"Ошибка Markdown разметки. Отправляю как текст: {e}")
216
+ await status.edit_text(
217
+ clean_text[:4090],
218
+ reply_markup=build_inline_keyboard(buttons)
219
+ )
220
 
221
  async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
222
  query = update.callback_query
 
227
  async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE):
228
  logger.error(f"Error: {context.error}")
229
 
230
+ # ============================================================
231
+ # ВЕБ-СЕРВЕР ДЛЯ HUGGING FACE (ПРЕДОТВРАЩАЕТ TIMEOUT ОШИБКИ)
232
+ # ============================================================
233
+ async def health_check(request):
234
+ return web.Response(text="Бот INAI успешно работает!")
235
+
236
+ async def start_web_server():
237
+ app = web.Application()
238
+ app.router.add_get('/', health_check)
239
+ runner = web.AppRunner(app)
240
+ await runner.setup()
241
+ site = web.TCPSite(runner, '0.0.0.0', 7860)
242
+ await site.start()
243
+ logger.info("✅ Локальный веб-сервер запущен на порту 7860 (Hugging Face счастлив)")
244
+
245
+ # Хук, который запускает веб-сервер одновременно с ботом
246
+ async def post_init(application: Application):
247
+ asyncio.create_task(start_web_server())
248
+
249
  # ============================================================
250
  # MAIN - ЗАПУСК
251
  # ============================================================
 
253
  def main():
254
  print("🚀 Starting INAI Bot on Hugging Face...")
255
 
256
+ # Настройка с МАКСИМАЛЬНЫМИ таймаутами
257
  application = (
258
  Application.builder()
259
  .token(BOT_TOKEN)
260
+ .post_init(post_init) # Запуск веб-сервера для HF
261
  .connect_timeout(60.0)
262
  .read_timeout(60.0)
263
  .write_timeout(60.0)